
tester si c'est soi-même qu'on modifie, pour tester si on peut bien altérer l'objet concerné. Création des objets en deux temps (on crée l'objet Crans, puis on l'enregistre dans ldap après test des droits. Changement de méthode de binding : le binding nominatif va être bien trop complexe à implémenter, on va donc faire autrement...
833 lines
24 KiB
Python
833 lines
24 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, sys, netaddr, time
|
|
from unicodedata import normalize
|
|
from crans_utils import format_tel, format_mac, mailexist, validate_name
|
|
|
|
sys.path.append("/usr/scripts/gestion")
|
|
import config, annuaires_pg
|
|
from ridtools import Rid
|
|
|
|
### 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"
|
|
parent = u"parent"
|
|
soi = u"soi"
|
|
responsable = u"responsable"
|
|
|
|
TOUS_DROITS = [nounou, apprenti, bureau, tresorier, imprimeur, moderateur, multimachines, cableur]
|
|
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 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 Attr(object):
|
|
"""La liste des droits qui suffisent à avoir le droit de modifier la valeur"""
|
|
legend = "Human-readable description of attribute"
|
|
singlevalue = None
|
|
optional = None
|
|
conn = None
|
|
unique = False
|
|
|
|
can_modify = [nounou]
|
|
|
|
can_view = [nounou, apprenti, soi, parent, responsable]
|
|
"""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.
|
|
|
|
val: valeur de l'attribut
|
|
ldif: objet contenant l'attribut (permet de faire les validations sur l'environnement)
|
|
ctxt_check: effectue les validations
|
|
"""
|
|
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"""
|
|
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__)
|
|
own_values = ldif.get(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('%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_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 _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...)"""
|
|
### On l'implémente dans les classes filles !
|
|
pass
|
|
|
|
def is_modifiable(self, liste_droits):
|
|
"""
|
|
L'attribut est-il modifiable par un des droits dans liste_droits ?
|
|
"""
|
|
return set(liste_droits).isdisjoint(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', '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 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']
|
|
|
|
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 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 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']:
|
|
res = self.conn.search('(|(mail=%s)(mailAlias=%s)(canonicalAlias=%s))' % ((str(self),)*3))
|
|
if res:
|
|
raise ValueError("Mail déjà existant", [r.dn for r in res])
|
|
|
|
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)" % ldif)
|
|
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) ])
|
|
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 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.append(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 ['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 = False
|
|
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 = Rid(rid= ldif['rid'][0]).ipv4()
|
|
self.value = netaddr.ip.IPAddress(ip)
|
|
|
|
def __unicode__(self):
|
|
return unicode(self.value)
|
|
|
|
|
|
class mid(Attr):
|
|
singlevalue = True
|
|
optional = False
|
|
unique = True
|
|
legend = "Identifiant de machine"
|
|
category = 'id'
|
|
|
|
def parse_value(self, mid, ldif):
|
|
self.value = mid
|
|
|
|
def __unicode__(self):
|
|
return unicode(int(self.value))
|
|
|
|
class rid(Attr):
|
|
singlevalue = True
|
|
optional = False
|
|
unique = True
|
|
legend = "Identifiant réseau de machine"
|
|
category = 'id'
|
|
can_modify = [nounou]
|
|
|
|
def parse_value(self, rid, ldif):
|
|
self.value = Rid(rid = int(rid))
|
|
|
|
def __unicode__(self):
|
|
return unicode(int(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 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 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'
|
|
|
|
class responsable(Attr):
|
|
singlevalue = True
|
|
optional = True
|
|
legend = u"Responsable du club"
|
|
category = 'perso'
|
|
can_modify = [nounou]
|
|
|
|
def get_respo(self):
|
|
if self.value == None:
|
|
self.value=self.conn.search('aid=%s' % self.__resp)[0]
|
|
return self.value
|
|
|
|
def parse_value(self, resp, ldif):
|
|
self.__resp=resp
|
|
self.value = property(self.get_respo)
|
|
|
|
def __unicode__(self):
|
|
return self.__resp
|
|
|
|
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'pc':
|
|
self.value = u'cp'
|
|
else:
|
|
self.value = ctrl
|
|
|
|
|
|
### 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,
|
|
'tel' : tel,
|
|
'paiement' : paiement,
|
|
'controle': controle,
|
|
'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,
|
|
'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,
|
|
'info': info,
|
|
'homepageAlias': homepageAlias,
|
|
'charteMA': charteMA,
|
|
'mail' : mail,
|
|
'postalAddress': postalAddress,
|
|
|
|
# {posix,shadow}Account
|
|
'homeDirectory': homeDirectory,
|
|
'loginShell': loginShell,
|
|
'uidNumber': uidNumber,
|
|
'gecos': gecos,
|
|
'gidNumber': gidNumber
|
|
}
|