1042 lines
30 KiB
Python
1042 lines
30 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
|
||
import random
|
||
import string
|
||
|
||
### 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:
|
||
if not isinstance(val, unicode):
|
||
val = val.decode('utf-8')
|
||
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
|
||
self._iterator = None
|
||
|
||
def __getitem__(self, attr):
|
||
values = super(AttrsDict, self).__getitem__(attr)
|
||
if not isinstance(values, list):
|
||
values = [ values ]
|
||
output = []
|
||
for val in values:
|
||
output.append(attrify(val , attr, self._conn, self._parent))
|
||
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
|
||
|
||
def items(self):
|
||
return [(key, self[key]) for key in self]
|
||
|
||
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 [str(o) for o 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 [str(o) for o 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()
|
||
names = val.split('.')
|
||
for name in names:
|
||
if not re.match('^[a-z0-9](-*[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(str(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'
|
||
|
||
def parse_value(self, val):
|
||
if len(val) in [10, 22]:
|
||
self.value = val
|
||
else:
|
||
val = u'auto'
|
||
if val == u"auto":
|
||
self.value = u''.join( random.choice(filter(lambda x: x != 'l' and x != 'o', string.lowercase) + filter(lambda x: x != '1' and x != '0', string.digits)) for i in range(10))
|
||
|
||
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',
|
||
'/usr/sbin/nologin'
|
||
'']
|
||
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 = True
|
||
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
|
||
|
||
## Penser à remplacer ça par un dictionnaire en compréhension quand on sera sous wheezy partout
|
||
#: Dictionnaire mappant "nom de l'atttribut" -> classe permettant de l'instancier
|
||
CRANS_ATTRIBUTES = {}
|
||
for (k, v) in locals().items():
|
||
if type(v) == type and issubclass(v, Attr):
|
||
CRANS_ATTRIBUTES[k] = v
|