1076 lines
31 KiB
Python
1076 lines
31 KiB
Python
#!/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],
|
||
}
|
||
|
||
|
||
def attrify(val, attr, ldif, conn, ctxt_check = True):
|
||
"""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)
|
||
|
||
|
||
class AttrsDict(dict) :
|
||
def __init__(self, conn, ldif = {}, check_ctxt = True):
|
||
super(AttrsDict, self).__init__(ldif)
|
||
self._conn = conn
|
||
self._ldif = ldif
|
||
self._check_ctxt = check_ctxt
|
||
|
||
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._ldif, self._conn, self._check_ctxt))
|
||
else:
|
||
output.append(val)
|
||
self[attr] = output
|
||
return output
|
||
|
||
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)
|
||
* ``ctxt_check`` : effectue les validations
|
||
"""
|
||
legend = "Human-readable description of attribute"
|
||
singlevalue = None
|
||
optional = None
|
||
conn = None
|
||
unique = False
|
||
|
||
can_modify = [nounou]
|
||
"""La liste des droits qui suffisent à avoir le droit de modifier la valeur"""
|
||
|
||
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."""
|
||
|
||
category = 'other'
|
||
"""Catégorie de l'attribut (pour affichage futur)"""
|
||
|
||
def __init__(self, val, ldif, conn, ctxt_check):
|
||
"""Crée un nouvel objet représentant un attribut. """
|
||
self.ctxt_check=ctxt_check
|
||
#self.value = None
|
||
self.conn = conn
|
||
assert isinstance(val, unicode)
|
||
self.parse_value(val, ldif)
|
||
if ctxt_check:
|
||
self.validate(ldif)
|
||
|
||
def parse_value(self, val, ldif):
|
||
"""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 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):
|
||
"""Vérifie l'unicité dans la base de la valeur (``mailAlias``, ``chbre``,
|
||
etc...)"""
|
||
attr = self.__class__.__name__
|
||
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])
|
||
|
||
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é"
|
||
|
||
can_modify = []
|
||
""" Personne ne doit modifier de classe """
|
||
|
||
can_view = [nounou, apprenti]
|
||
""" Internal purpose (et à fin pédagogique) """
|
||
|
||
def parse_value(self, val, ldif):
|
||
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, ldif):
|
||
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, ldif):
|
||
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
|
||
|
||
can_modify = []
|
||
""" Personne ne devrait modifier un attribut d'identification """
|
||
|
||
can_view = [nounou, apprenti, 'Cableurs']
|
||
|
||
def parse_value(self, val, ldif):
|
||
self.value = int(val)
|
||
|
||
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, val, ldif):
|
||
if u'club' in ldif['objectClass']:
|
||
self.value = validate_name(val,"0123456789\[\]")
|
||
else:
|
||
self.value = validate_name(val)
|
||
|
||
class prenom(Attr):
|
||
singlevalue = True
|
||
optional = False
|
||
legend = u"Prénom"
|
||
category = 'perso'
|
||
can_modify = [nounou, cableur]
|
||
|
||
def parse_value(self, val, ldif):
|
||
self.value = validate_name(val)
|
||
|
||
class compteWiki(Attr):
|
||
singlevalue = False
|
||
optional = True
|
||
legend = u"Compte WiKi"
|
||
category = 'perso'
|
||
can_modify = [nounou, cableur, soi]
|
||
|
||
def parse_value(self, val, ldif):
|
||
self.value = validate_name(val)
|
||
# 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, val, ldif):
|
||
self.value = format_tel(val)
|
||
if len(self.value) == 0:
|
||
raise ValueError("Numéro de téléphone invalide (%r)" % val)
|
||
|
||
class yearAttr(intAttr):
|
||
singlevalue = False
|
||
optional= True
|
||
|
||
def parse_value(self, val, ldif):
|
||
if int(val) < 1998:
|
||
raise ValueError("Année invalide (%r)" % val)
|
||
self.value = int(val)
|
||
|
||
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):
|
||
attr = self.__class__.__name__
|
||
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, ldif):
|
||
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, 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
|
||
|
||
class mailAlias(mail):
|
||
singlevalue = False
|
||
optional = True
|
||
unique = True
|
||
legend = u"Alias 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
|
||
|
||
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, val, ldif):
|
||
# who cares
|
||
self.value = val
|
||
|
||
|
||
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, 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
|
||
return
|
||
else:
|
||
raise ValueError("Club devrait etre en XclN, pas en %r" % val)
|
||
|
||
if val in (u"EXT", u"????"):
|
||
self.value = val
|
||
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:])
|
||
|
||
self.value = val
|
||
|
||
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, val, ldif):
|
||
# 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()
|
||
|
||
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, ldif):
|
||
# 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.):
|
||
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
|
||
not re.match('[a-z][-_a-z0-9]+', name)):
|
||
raise ValueError("Nom d'hote invalide %r" % dns)
|
||
self.value = dns
|
||
|
||
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):
|
||
attr = self.__class__.__name__
|
||
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, ldif):
|
||
self.value = format_mac(mac)
|
||
|
||
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, ldif):
|
||
if ip == '<automatique>':
|
||
ip = ip4_of_rid(ldif['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, val, ldif):
|
||
ip = ip6_of_mac(ldif['macAddress'][0], int(ldif['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, ldif):
|
||
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, ldif):
|
||
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, val, ldif):
|
||
if val == u'N/A N/A':
|
||
self.value = u"N/A°N N/A°E"
|
||
else:
|
||
lat, lon = val.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, val, ldif):
|
||
pass
|
||
|
||
class portAttr(Attr):
|
||
singlevalue = False
|
||
optional = True
|
||
legend = u'Ouverture de port'
|
||
category = 'firewall'
|
||
can_modify = [nounou]
|
||
|
||
def parse_value(self, port, ldif):
|
||
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, ldif):
|
||
### Tu es une Nounou, 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, ldif):
|
||
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, val):
|
||
"""
|
||
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):
|
||
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, ldif):
|
||
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, val):
|
||
"""
|
||
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, val, ldif):
|
||
self.__value = val
|
||
|
||
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, bl, ldif):
|
||
bl_debut, bl_fin, bl_type, bl_comm = bl.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, 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()
|
||
|
||
class homeDirectory(Attr):
|
||
singlevalue=True
|
||
optional = True
|
||
unique = True
|
||
legend="Le chemin du home de l'adhérent"
|
||
|
||
def parse_value(self, home, ldif):
|
||
uid = ldif['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]))
|
||
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, ldif):
|
||
#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 self.ctxt_check and (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, ldif):
|
||
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, ldif):
|
||
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
|
||
|
||
### 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,
|
||
'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
|
||
}
|