683 lines
26 KiB
Python
683 lines
26 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# LC_LDAP.PY-- LightWeight CransLdap
|
|
#
|
|
# Copyright (C) 2010 Cr@ns <roots@crans.org>
|
|
# Author: Antoine Durand-Gasselin <adg@crans.org>
|
|
# All rights reserved.
|
|
#
|
|
# 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.
|
|
|
|
from __future__ import with_statement
|
|
import os, sys, ldap, ldap.modlist, re, netaddr, datetime, copy, time
|
|
sys.path.append('/usr/scripts/gestion')
|
|
import config, crans_utils
|
|
|
|
uri = 'ldapi:///' #'ldap://ldap.adm.crans.org/'
|
|
base_dn = 'ou=data,dc=crans,dc=org'
|
|
base_lock = 'ou=lock,dc=crans,dc=org'
|
|
|
|
def is_actif(sanction):
|
|
"""Retourne True ou False suivant si la sanction fournie (chaîne
|
|
venant de blacklist) est active ou non
|
|
"""
|
|
bl = sanction.split('$')
|
|
now = time.time()
|
|
debut = int(bl[0])
|
|
if bl[1] == '-':
|
|
fin = now + 1
|
|
else:
|
|
fin = int(bl[1])
|
|
return debut < now and fin > now
|
|
|
|
def uldif_to_ldif(uldif):
|
|
"""Prend en argument un dico ldif, et vérifie que toutes les
|
|
valeurs sont bien des unicodes, les converti alors en chaînes
|
|
utf-8, renvoie un ldif"""
|
|
ldif = {}
|
|
for attr, vals in uldif.items():
|
|
ldif[attr] = [ unicode.encode(val, 'utf-8') for val in uldif[attr] ]
|
|
return ldif
|
|
|
|
def ldif_to_uldif(ldif):
|
|
'Prend en argument un dico ldif, et décode toutes les chaînes en utf-8'
|
|
uldif = {}
|
|
for attr, vals in ldif.items():
|
|
uldif[attr] = [ unicode(val, 'utf-8') for val in ldif[attr] ]
|
|
return uldif
|
|
|
|
class lc_ldap(ldap.ldapobject.LDAPObject):
|
|
def __init__(self, dn=None, user=None, cred=None, uri=uri):
|
|
"""Initialise la connexion ldap,
|
|
- En authentifiant avec dn et cred s'ils sont précisés
|
|
- Si dn n'est pas précisé, mais que user est précisé, récupère
|
|
le dn associé à l'uid user, et effectue l'authentification
|
|
avec ce dn et cred
|
|
- Sinon effectue une authentification anonyme
|
|
"""
|
|
|
|
ldap.ldapobject.LDAPObject.__init__(self, uri)
|
|
|
|
if user and not re.match('[a-z_][a-z0-9_-]*', user):
|
|
raise ValueError('Invalid user name: %s' % user)
|
|
|
|
if user and not dn:
|
|
self.simple_bind_s(base_dn)
|
|
res = self.search_s('uid=%s' % user)
|
|
if len(res) < 1:
|
|
raise ldap.INVALID_CREDENTIALS({'desc': 'No such user: %s' %s })
|
|
elif len(res) > 1:
|
|
raise ldap.INVALID_CREDENTIALS({'desc': 'Too many matches: uid=%s' %s })
|
|
else:
|
|
dn = res[0][0]
|
|
if dn:
|
|
self.conn = self.bind_s(dn, cred)
|
|
else:
|
|
self.conn = self.simple_bind_s()
|
|
|
|
def search(self, filter, mode='ro', dn= base_dn, scope= 2, sizelimit=400):
|
|
res = self.search_ext_s(dn, scope, filter, sizelimit=sizelimit)
|
|
return [ CransLdapObject(self, r[0], mode=mode) for r in res ]
|
|
|
|
def allMachines(self):
|
|
"""Renvoie la liste de toutes les machines,
|
|
Conçue pour s'éxécuter le plus rapidement possible"""
|
|
res = {}
|
|
machines = []
|
|
for dn, attrs in self.search_s(base_dn, scope=2):
|
|
res[dn] = attrs
|
|
for dn, attrs in res.items():
|
|
if dn.startswith('mid='):
|
|
m = CransLdapObject(self, dn, ldif = attrs)
|
|
parent_dn = dn.split(',', 1)[1]
|
|
m._proprio = CransLdapObject(self, parent_dn, res[parent_dn])
|
|
machines.append(m)
|
|
return machines
|
|
|
|
def newMachine(self, parent, realm, uldif):
|
|
"""Crée une nouvelle machine"""
|
|
raise NotImplementedError()
|
|
|
|
def newAdherent(self, uldif):
|
|
"""Crée un nouvel adhérent"""
|
|
aid = uldif.setdefault('aid', [ unicode(self._find_id('aid')) ])
|
|
# XXX - autres tests
|
|
return self._create_entity('aid=%s,%s' % (aid[0], base_dn), uldif)
|
|
|
|
def newClub(self, uldif):
|
|
"""Crée un nouveau club"""
|
|
raise NotImplementedError()
|
|
|
|
def newFacture(self, uldif):
|
|
"""Crée une nouvelle facture"""
|
|
raise NotImplementedError()
|
|
|
|
def _create_entity(self, dn, uldif):
|
|
'''Crée une nouvelle entité ldap en dn, avec attributs ldif:
|
|
uniquement en unicode'''
|
|
lock = CransLock(self)
|
|
for item in ['aid', 'uid', 'chbre', 'mailAlias', 'canonicalAlias',
|
|
'fid', 'cid', 'mid', 'macAddress', 'host', 'hostAlias' ]:
|
|
for val in uldif.get(item, []):
|
|
lock.add(item, val)
|
|
uldif['historique'] = [ self._hist('Création')]
|
|
ldif = uldif_to_ldif(uldif)
|
|
modlist = ldap.modlist.addModlist(ldif)
|
|
with lock:
|
|
print dn, modlist
|
|
self.add_s(dn, modlist)
|
|
return CransLdapObject(self, dn, mode='w')
|
|
|
|
def _find_id(self, attr, plage = xrange(1, 32000)):
|
|
'''Trouve un <attr>id libre dans plage'''
|
|
res = self.search_s(base_dn, 2, '%s=*' % attr, attrlist = [attr])
|
|
nonfree = [ int(r[1].get(attr)[0]) for r in res if r[1].get(attr) ]
|
|
nonfree.sort()
|
|
|
|
for id in plage:
|
|
if nonfree and nonfree[0] <= id:
|
|
while nonfree and nonfree[0] <= id:
|
|
nonfree = nonfree[1:]
|
|
else:
|
|
break
|
|
else:
|
|
raise EnvironmentError(u'Aucun %s libre dans la plage [%d, %d]' %
|
|
(attr, plage[0], id))
|
|
return id
|
|
|
|
def _hist(self, msg):
|
|
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M : ')
|
|
return unicode(now) + msg
|
|
|
|
# ? def reconnect(self, conn=None):
|
|
|
|
|
|
class CransLock:
|
|
|
|
def __init__(self, conn):
|
|
self.conn = conn
|
|
self.locks = {}
|
|
self._active = []
|
|
|
|
def __enter__(self):
|
|
self.lock()
|
|
|
|
def __exit__(self, *args):
|
|
# XXX - connecter correctement les tracebacks.
|
|
print "exiting with exception", args
|
|
self.release()
|
|
return True
|
|
|
|
def add(self, item, valeur):
|
|
'''rajoute un lock, après avoir vérifié qu'il peut être posé'''
|
|
try:
|
|
locked = self._islocked(item, valeur)
|
|
if locked:
|
|
raise EnvironmentError(u'Object déjà locké', locked)
|
|
except ldap.NO_SUCH_OBJECT:
|
|
pass
|
|
|
|
locked_values = self.locks.get(item, [])
|
|
if valeur not in locked_values:
|
|
locked_values.append(valeur)
|
|
self.locks[item] = locked_values
|
|
|
|
def remove(self, item, valeur):
|
|
'''Enlève un lock'''
|
|
self.locks[item].remove(valeur)
|
|
|
|
def lock(self):
|
|
'''Essaie de prendre tous les verrous'''
|
|
items = self.locks.items()
|
|
items.sort()
|
|
try:
|
|
for item, valeurs in items:
|
|
for valeur in valeurs:
|
|
self._lockitem(item, valeur)
|
|
except Exception, e:
|
|
# XXX - connecter proprement les traceback
|
|
self.release()
|
|
raise e
|
|
|
|
def release(self):
|
|
'''Relâche tous les verrous'''
|
|
exceptions = []
|
|
print "releasing", self._active
|
|
for item in self._active[:]:
|
|
try:
|
|
self._releaseitem(item)
|
|
except Exception, e:
|
|
exceptions.append(e)
|
|
if len(exceptions) == 1:
|
|
# XXX - connecter proprement les tracebacks
|
|
raise exceptions[0]
|
|
elif len(exceptions) > 1:
|
|
raise Exception(exceptions)
|
|
|
|
def _islocked(self, item, valeur):
|
|
# XXX - return self.conn.search_s(base_dn, 2, '%s=%s' % (item, valeur))
|
|
return self.conn.search_s('%s=%s,%s' % (item, valeur, base_lock), 0)
|
|
|
|
def _lockitem(self, item, valeur):
|
|
u"""
|
|
Lock un item avec la valeur valeur, les items possibles
|
|
peuvent être :
|
|
aid $ chbre $ mail $ mailAlias $ canonicalAlias $
|
|
mid $ macAddress $ host $ hostAlias $ ipHostNumber
|
|
Retourne le dn du lock
|
|
"""
|
|
|
|
valeur = valeur.encode('utf-8')
|
|
|
|
lock_dn = '%s=%s,%s' % (item, valeur, base_lock)
|
|
lockid = '%s-%s' % ('localhost', os.getpid())
|
|
modlist = ldap.modlist.addModlist({ 'objectClass': 'lock',
|
|
'lockid': lockid,
|
|
item: valeur })
|
|
print "locking", lock_dn
|
|
try:
|
|
self.conn.add_s(lock_dn, modlist)
|
|
except ldap.ALREADY_EXISTS:
|
|
# # Pas de chance, le lock est déja pris
|
|
# try:
|
|
# res = self.conn.search_s(lock_dn, 2, 'objectClass=lock')[0]
|
|
# l = res[1]['lockid'][0]
|
|
# except: l = '%s-1' % hostname
|
|
# if l != lockid:
|
|
# # C'est locké par un autre process que le notre
|
|
# # il tourne encore ?
|
|
# if l.split('-')[0] == hostname and os.system('ps %s > /dev/null 2>&1' % l.split('-')[1] ):
|
|
# # Il ne tourne plus
|
|
# self._releaseitem(res[0]) # delock
|
|
# return self._lockitem(item, valeur) # relock
|
|
raise EnvironmentError(u'Objet (%s=%s) locké, patienter.' % (item, valeur), l)
|
|
self._active.append(lock_dn)
|
|
return lock_dn
|
|
|
|
def _releaseitem(self, lockdn):
|
|
u"""Destruction d'un lock"""
|
|
# Mettre des verifs ?
|
|
print "releasing", lockdn
|
|
self._active.remove(lockdn)
|
|
self.conn.delete_s(lockdn)
|
|
|
|
class CransLdapObject:
|
|
mode = 'ro'
|
|
|
|
attrs = None # Contient un dico uldif qui doit représenter ce qui
|
|
# est dans la base
|
|
|
|
_modifs = None # C'est là qu'on met les modifications
|
|
|
|
def __init__(self, conn, dn, mode='ro', ldif = None):
|
|
'''Créé une instance d'un objet Crans (machine, adhérent,
|
|
etc...) à ce dn, si ldif est précisé, n'effectue pas de
|
|
recherche dans la base ldap.
|
|
'''
|
|
if not isinstance(conn, lc_ldap):
|
|
raise TypeError(u"conn doit être une instance de lc_ldap")
|
|
self.conn = conn
|
|
|
|
if ldif:
|
|
self.dn = dn
|
|
# /!\ attention, on a pas un uldif (rapidité...)
|
|
self.attrs = ldif
|
|
self.__class__ = eval(self.attrs['objectClass'][0])
|
|
elif dn == base_dn:
|
|
self.__class__ = AssociationCrans
|
|
else:
|
|
self.mode = mode
|
|
res = conn.search_s(dn, 0)
|
|
if not res:
|
|
raise ValueError ('objet inexistant: %s' % dn)
|
|
self.dn, self.attrs = res[0]
|
|
self.attrs = ldif_to_uldif(self.attrs)
|
|
self.__class__ = eval(self.attrs['objectClass'][0])
|
|
self._modifs = copy.deepcopy(self.attrs)
|
|
|
|
def save(self):
|
|
"Enregistre les modifications"
|
|
if self.mode != 'w':
|
|
raise EnvironmentError(u"Objet en lecture seule, réessayer en lecture/écriture")
|
|
|
|
# Vérifications et Historique
|
|
histo = self._gen_hist(self._modifs)
|
|
self._modifs['historique'] += histo
|
|
|
|
# unicode -> utf-8
|
|
ldif = uldif_to_ldif(self._modifs)
|
|
orig_ldif = uldif_to_ldif(self.attrs)
|
|
|
|
# modifications
|
|
modlist = ldap.modlist.modifyModlist(orig_ldif, ldif)
|
|
self.conn.modify_s(self.dn, modlist)
|
|
|
|
# Vérification des modifications
|
|
self.attrs = ldif_to_uldif(self.conn.search_s(self.dn, 0)[0][1])
|
|
if self.attrs != self._modifs:
|
|
raise EnvironmentError(u"Les modifications apportées à l'objet %s n'ont pas été correctement sauvegardées\nexpected = %s, found = %s" % (self.dn, self._modifs, self.attrs))
|
|
|
|
def _gen_hist(self, modifs):
|
|
"""Vérifie la correction des modifs et genère l'historique des
|
|
modifications apportées"""
|
|
histo = []
|
|
for field in self.ufields:
|
|
if len(modifs.get(field, [])) != 1:
|
|
raise ValueError('%s doit avoir exactement une valeur' % field)
|
|
|
|
for field in self.ofields:
|
|
if len(modifs.get(field, [])) > 1:
|
|
raise ValueError('%s doit avoir au maximum une valeur' % field)
|
|
if modifs.get(field, []) != self.attrs.get(field, []):
|
|
if modifs.get(field, []) == []:
|
|
msg = u"[%s] %s -> RESET" % (field, self.attrs[field][0])
|
|
elif self.attrs.get(field, []) == []:
|
|
msg = u"[%s] := %s"(field, modifs[field][0])
|
|
else:
|
|
msg = u"[%s] %s -> %s" % (field, self.attrs[field][0], modifs[field][0])
|
|
histo.append(self.conn._hist(msg))
|
|
|
|
for field in self.xfields + self.ufields:
|
|
if modifs.get(field, []) != self.attrs.get(field, []):
|
|
msg = u"[%s] %s -> %s" % (field, u'; '.join(self.attrs[field]), u'; '.join(modifs[field]))
|
|
histo.append(self.conn._hist(msg))
|
|
|
|
for field in self.mfields:
|
|
oldvals = self.attrs.get(field, [])
|
|
newvals = modifs.get(field, [])
|
|
olds = set(oldvals)
|
|
news = set(newvals)
|
|
if oldvals == newvals:
|
|
continue
|
|
elif olds != news:
|
|
adds = ''.join([ '+' + val for val in news - olds])
|
|
diff = ''.join([ '-' + val for val in olds - news])
|
|
msg = u'[%s]%s%s' % ( field, adds, diff)
|
|
elif olds == news and len(oldvals) == len(newvals) == len(olds):
|
|
msg = u"[%s].shuffle()" % field
|
|
else:
|
|
raise ValueError(u"Les valeurs pour %s : %s -> %s ne semblent pas différentes" % (field, oldvals, newvals))
|
|
histo.append(self.conn._hist(msg))
|
|
|
|
return histo
|
|
|
|
def blacklist_actif(self):
|
|
u"""Vérifie si l'instance courante est blacklistée.
|
|
Retourne les sanctions en cours (liste).
|
|
Retourne une liste vide si aucune sanction en cours.
|
|
"""
|
|
return self.blacklist_all()[0].keys()
|
|
|
|
def blacklist_all(self):
|
|
u"""Vérifie si l'instance courante est blacklistée ou a été
|
|
blacklistée. Retourne les sanctions en cours sous la forme
|
|
d'un couple de deux dictionnaires (l'un pour les sanctions
|
|
actives, l'autre pour les inactive), chacun ayant comme
|
|
clef la sanction et comme valeur une liste de couple de
|
|
dates (en secondes depuis epoch) correspondant aux
|
|
différentes périodes de sanctions.
|
|
|
|
ex: {'upload': [(1143336210, 1143509010), ...]}
|
|
"""
|
|
bl_liste = self.attrs.get('blacklist', [])
|
|
if isinstance(self, machine): # Blist du propriétaire
|
|
bl_liste += proprio().blacklist()
|
|
|
|
actifs = {}; inactifs = {}
|
|
for sanction in bl_liste:
|
|
f = sanction.split('$')
|
|
if is_actif(sanction):
|
|
actifs.setdefault(f[2], []).append((f[0], f[1]))
|
|
else:
|
|
inactifs.setdefault(f[2], []).append((f[0], f[1]))
|
|
return (actifs, inactifs)
|
|
|
|
def blacklist(self, new=None):
|
|
u"""
|
|
Blacklistage de la ou de toutes la machines du propriétaire
|
|
* new est une liste de 4 termes :
|
|
[debut_sanction, fin_sanction, sanction, commentaire]
|
|
* début et fin sont le nombre de secondes depuis epoch
|
|
* pour un début ou fin immédiate mettre now
|
|
* pour une fin indéterminée mettre '-'
|
|
Les données sont stockées dans la base sous la forme :
|
|
debut$fin$sanction$commentaire
|
|
Pour modifier une entrée donner un tuple de deux termes :
|
|
(index dans blacklist à modifier, nouvelle liste),
|
|
l'index étant celui dans la liste retournée par blacklist().
|
|
"""
|
|
Blist = self.attrs.setdefault('blacklist', [])[:]
|
|
if new == None:
|
|
return Blist
|
|
|
|
if type(new) == tuple:
|
|
# Modification
|
|
index = new[0]
|
|
new = new[1]
|
|
if new == '':
|
|
Blist.pop(index)
|
|
return Blist
|
|
else:
|
|
index = -1
|
|
|
|
if type(new) != list or len(new) != 4:
|
|
raise TypeError
|
|
|
|
# Verification que les dates sont OK
|
|
if new[0] == 'now':
|
|
debut = new[0] = int(time.time())
|
|
else:
|
|
try: debut = new[0] = int(new[0])
|
|
except: raise ValueError(u'Date de début blacklist invalide')
|
|
|
|
if new[1] == 'now':
|
|
fin = new[1] = int(time.time())
|
|
elif new[1] == '-':
|
|
fin = -1
|
|
else:
|
|
try: fin = new[1] = int(new[1])
|
|
except: raise ValueError(u'Date de fin blacklist invalide')
|
|
|
|
if debut == fin:
|
|
raise ValueError(u'Dates de début et de fin identiques')
|
|
elif fin != -1 and debut > fin:
|
|
raise ValueError(u'Date de fin avant date de début')
|
|
|
|
# On dépasse la fin de sanction d'1min pour être sûr qu'elle est périmée.
|
|
fin = fin + 60
|
|
|
|
new_c = u'$'.join(map(str, new))
|
|
|
|
if index != -1:
|
|
Blist[index] = new_c
|
|
else:
|
|
Blist.append(new_c)
|
|
|
|
if Blist != self._modifs.get('blacklist'):
|
|
self._modifs['blacklist'] = Blist
|
|
if not hasattr(self, "_blacklist_restart"):
|
|
self._blacklist_restart = {}
|
|
restart = self._blacklist_restart.setdefault(new[2], [])
|
|
if debut not in restart:
|
|
restart.append(debut)
|
|
if fin != -1 and fin not in restart:
|
|
restart.append(fin)
|
|
|
|
return Blist
|
|
|
|
def __getattribute__(self, attr):
|
|
if self.__dict__.has_key(attr):
|
|
return self.__dict__[attr]
|
|
else:
|
|
if attr in self.ufields + self.ofields + self.mfields + self.xfields:
|
|
return _getormod_ldapattr(self, attr)
|
|
|
|
def get_ldapattr(self, attr):
|
|
"""Renvoie un attribut ldap de l'objet self"""
|
|
attrs = self._modifs.get(attr, self.attrs.get(attr,[]))
|
|
if len(attrs) == 1:
|
|
return attrs[0]
|
|
else: return attrs
|
|
|
|
def mod_ldapattr(self, attr, newVal, oldVal = None):
|
|
"""Modifie l'attribut attr ayant la valeur oldVal en newVal. Si
|
|
l'attribut attr n'a qu'une seule valeur, il n'est pas nécessaire
|
|
de préciser oldVal."""
|
|
assert isinstance(newVal, unicode)
|
|
attrs = self._modifs.get(attr, self.attrs[attr])[:]
|
|
if oldVal: # and oldVal in attrs:
|
|
attrs.remove(oldVal)
|
|
attrs.append(newVal)
|
|
self._modifs[attr] = attrs
|
|
elif len(attrs) == 1:
|
|
self._modifs[attr] = [newVal]
|
|
else:
|
|
raise ValueError(u"%s has multiple values, must specify oldVal")
|
|
|
|
def del_ldapattr(self, attr, val):
|
|
"""Supprime la valeur val de l'attribut attr"""
|
|
self._modifs.setdefault(attr, self.attrs.get(attr, [])[:]) .remove(newVal)
|
|
|
|
def set_ldapattr(self, attr, newVals):
|
|
"""Définit les nouvelles valeurs d'un attribut"""
|
|
if not isinstance(newVals, list):
|
|
newVals = [newVals]
|
|
for val in newVals: assert isinstance(val, unicode)
|
|
self._modifs[attr] = newVals
|
|
|
|
def add_ldapattr(self, attr, newVal):
|
|
"""Rajoute la valeur val à l'attribut attr"""
|
|
assert isinstance(newVal, unicode)
|
|
self._modifs.setdefault(attr, self.attrs.get(attr, [])[:]).append(newVal)
|
|
|
|
|
|
class proprio(CransLdapObject):
|
|
ufields = [ 'nom', 'chbre' ]
|
|
mfields = [ 'paiement', 'info', 'blacklist', 'controle']
|
|
ofields = []; xfields = []
|
|
_machines = None
|
|
def machines(self):
|
|
if self._machines == None:
|
|
self._machines = self.conn.search_s('mid=*', dn = self.dn, scope = 1)
|
|
for m in machines:
|
|
m._proprio = self
|
|
return self._machines
|
|
|
|
class machine(CransLdapObject):
|
|
_proprio = None
|
|
ufields = ['mid', 'macAddress', 'host', 'midType']
|
|
ofields = []
|
|
mfields = ['info', 'blacklist', 'hostAlias', 'exempt',
|
|
'portTCPout', 'portTCPin', 'portUDPout', 'portUDPin']
|
|
xfields = ['ipHostNumber']
|
|
def proprio(self):
|
|
parent_dn = self.dn.split(',', 1)[1]
|
|
self._proprio = CransLdapObject(self.conn, parent_dn, self.mode)
|
|
return self._proprio
|
|
|
|
|
|
class AssociationCrans(proprio): pass
|
|
|
|
class adherent(proprio):
|
|
ufields = proprio.ufields + ['aid', 'prenom', 'tel', 'mail', 'mailInvalide']
|
|
ofields = proprio.ofields + ['charteMA', 'adherentPayant', 'typeAdhesion',
|
|
'canonicalAlias', 'solde', 'contourneGreylist',
|
|
'rewriteMailHeaders', 'derniereConnexion',
|
|
'homepageAlias']
|
|
mfields = proprio.mfields + ['carteEtudiant', 'mailAlias', 'droits' ]
|
|
xfields = ['etudes', 'postalAddress']
|
|
|
|
class club(proprio):
|
|
ufields = ['cid', 'responsable']
|
|
mfields = ['imprimeurClub']
|
|
|
|
class machineFixe(machine): pass
|
|
|
|
class machineWifi(machine):
|
|
ufields = machine.ufields + ['ipsec']
|
|
|
|
class machineCrans(machine):
|
|
ufields = machine.ufields + ['prise']
|
|
ofields = machine.ofields + ['nombrePrises']
|
|
|
|
class borneWifi(machine):
|
|
ufields = machine.ufields + ['canal', 'puissane', 'hotspot',
|
|
'prise', 'positionBorne', 'nvram']
|
|
|
|
class facture(CransLdapObject):
|
|
ufields = ['fid', 'modePaiement', 'recuPaiement']
|
|
|
|
class service(CransLdapObject): pass
|
|
|
|
class lock(CransLdapObject): pass
|
|
|
|
|
|
MODIFIABLE_ATTRS = [ 'tel', 'chbre', 'mailAlias', 'loginShell' ]
|
|
|
|
|
|
|
|
|
|
CRANS_ATTRIBUTES = {
|
|
'nom' : { 'attr' : 'nom',
|
|
'hname' : 'Nom',
|
|
'isunique' : True },
|
|
'prenom' : { 'attr' : 'prenom',
|
|
'hname' : u'Prénom',
|
|
'isunique' : True },
|
|
'tel' : { 'attr' : 'tel',
|
|
'hname' : 'Téléphone',
|
|
'isunique' : True },
|
|
'paiement' : { 'attr' : 'paiement',
|
|
'hname' : u'Années de cotisations',
|
|
'isunique' : False },
|
|
'carteEtudiant' : { 'atttr' : 'carteEtudiant',
|
|
'hname' : u'Carte fournie pour les années',
|
|
'isunique' : False },
|
|
'mailAlias' : { 'attr' : 'mailAlias',
|
|
'hname' : 'Alias mail',
|
|
'isunique' : False },
|
|
'canonicalAlias' : { 'attr' : 'canonicalAlias',
|
|
'hname' : 'Alias mail canonique',
|
|
'isunique' : True },
|
|
'etudes' : { 'attr' : 'etudes',
|
|
'hname' : u'Études suivies',
|
|
'isunique' : False },
|
|
'chbre' : { 'attr' : 'chbre',
|
|
'hname' : 'Chambre',
|
|
'isunique' : True },
|
|
'droits' : { 'attr' : 'droits',
|
|
'hname' : 'Droits',
|
|
'isunique' : False },
|
|
'solde' : { 'attr' : 'solde',
|
|
'hname' : "Solde sur le compte d'impression",
|
|
'isunique' : True },
|
|
|
|
|
|
'mid' : { 'attr' : 'mid',
|
|
'hname' : 'Identifiant de machine',
|
|
'isunique' : True },
|
|
'hostAlias' : { 'attr' : 'hostAlias',
|
|
'hname' : 'Alias de nom de machine',
|
|
'isunique' : False },
|
|
'ipsec' : { 'attr' : 'ipsec',
|
|
'hname' : 'Clef wifi',
|
|
'isunique' : True },
|
|
'puissance' : { 'attr' : 'puissance',
|
|
'hname' : u"Puissance d'émission de la borne wifi",
|
|
'isunique' : True },
|
|
'canal' : { 'attr' : 'canal',
|
|
'hname' : u"Canal d'émission de la borne wifi",
|
|
'isunique' : True },
|
|
'portTCPout' : { 'attr' : 'portTCPout',
|
|
'hname' : u"Port TCP ouvert vers l'extérieur",
|
|
'isunique' : False },
|
|
'portTCPin' : { 'attr' : 'portTCPin',
|
|
'hname' : u"Port TCP ouvert depuis l'extérieur",
|
|
'isunique' : False },
|
|
'portUDPout' : { 'attr' : 'portUDPout',
|
|
'hname' : u"Port UDP ouvert vers l'extérieur",
|
|
'isunique' : False },
|
|
'portUDPin' : { 'attr' : 'portUDPin',
|
|
'hname' : u"Port UDP ouvert depuis l'extérieur",
|
|
'isunique' : False },
|
|
'prise' : { 'attr' : 'prise',
|
|
'hname' : 'Prise sur laquelle est branchée la machine',
|
|
'isunique' : True },
|
|
|
|
|
|
'cid' : { 'attr' : 'cid',
|
|
'hname' : 'Identifiant de club',
|
|
'isunique' : True },
|
|
'responsable' : { 'attr' : 'responsable',
|
|
'hname' : 'Responsable du club',
|
|
'isunique' : True },
|
|
|
|
'blacklist' : {'attr' : 'blacklist',
|
|
'hname' : 'Historique des sanctions',
|
|
'isunique' : False },
|
|
'historique' : { 'attr' : 'historique',
|
|
'hname' : 'Historique des modifications',
|
|
'isunique' : False },
|
|
}
|
|
|