882 lines
26 KiB
Python
882 lines
26 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 annuaires_pg
|
|
|
|
### 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', '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 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 = False
|
|
legend = u"Compte WiKi"
|
|
|
|
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 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) ])
|
|
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 ['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, ip, 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 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'
|
|
|
|
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 = [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'cp':
|
|
self.value = u'pc'
|
|
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,
|
|
'compteWiki' : compteWiki,
|
|
'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,
|
|
'ip6HostNumber': ip6HostNumber,
|
|
'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
|
|
}
|