
Si l'adhrent n'a pas de compte ne pose pas de question. Sinon demande si le compte est utilis si oui conserve le login inchang si non dtruit le compte et en cre un nouveau darcs-hash:20051107000653-41617-8638a050728561810634ecd1e09c04917aca9987.gz
2559 lines
95 KiB
Python
Executable file
2559 lines
95 KiB
Python
Executable file
#! /usr/bin/env python
|
||
# -*- coding: iso-8859-15 -*-
|
||
|
||
"""
|
||
Définitions des classes de base du système de gestion des machines
|
||
et adhérents du crans
|
||
|
||
Copyright (C) Frédéric Pauget
|
||
Licence : GPLv2
|
||
"""
|
||
|
||
from socket import gethostname
|
||
import smtplib, sre, os, random, string, time, sys
|
||
import ldap, ldap.modlist
|
||
|
||
import config, annuaires, iptools, chgpass, user_tests, cPickle
|
||
from chgpass import chgpass
|
||
from affich_tools import coul, prompt
|
||
from time import sleep,localtime
|
||
|
||
try:
|
||
from secrets import ldap_password, ldap_auth_dn
|
||
except:
|
||
sys.stdout.write(coul('Warning : impossible de lire le fichier de secret !\n','jaune'))
|
||
sleep(2)
|
||
ldap_password = ''
|
||
ldap_auth_dn = ''
|
||
|
||
date_format='%d/%m/%Y %H:%M'
|
||
hostname = gethostname().split(".")[0]
|
||
if hostname == "vert":
|
||
anon_bind = 1 # Anonyme pour lecture seule
|
||
uri = 'ldapi://%2fvar%2frun%2fldapi/'
|
||
rw_uri = uri
|
||
elif hostname in [ 'sila' , 'pegase' ]:
|
||
anon_bind = 1 # Anonyme pour lecture seule
|
||
uri = 'ldapi://%2fvar%2frun%2fldapi/'
|
||
rw_uri = 'ldap://ldap.adm.crans.org/'
|
||
elif hostname == 'egon' and not '/usr/scripts/gestion/' in sys.argv[0]:
|
||
# pour les tests, les séminaires... sur egon
|
||
anon_bind = 1 # Anonyme pour lecture seule
|
||
uri = 'ldapi://%2fvar%2frun%2fldapi/'
|
||
rw_uri = uri
|
||
ldap_auth_dn='cn=admin,dc=crans,dc=org'
|
||
ldap_password='75bdb64f32'
|
||
else:
|
||
anon_bind = 0
|
||
uri = 'ldap://ldap.adm.crans.org/'
|
||
rw_uri = uri
|
||
|
||
smtpserv = "rouge.crans.org"
|
||
|
||
random.seed() # On initialise le générateur aléatoire
|
||
|
||
##################################################################################
|
||
### Items de la blackliste
|
||
blacklist_items = { u'bloq' : u'Bloquage total de tout services' ,
|
||
u'virus' : u'Bloquage sur squid',
|
||
u'upload' : u'Bloquage total accès extérieur',
|
||
u'autodisc' : u'Autodisconnect pour upload',
|
||
u'warez' : u'Bloquage sur squid',
|
||
u'p2p' : u'Sanction à définir'}
|
||
|
||
##################################################################################
|
||
### Droits possibles
|
||
droits_possibles = [ u'Nounou', u'Apprenti', u'Modérateur', u'Câbleur', u'Déconnecteur', u'WebRadio' , u'Imprimeur', u'MultiMachines', u'Contrôleur' ]
|
||
|
||
##################################################################################
|
||
### Variables internes diverses
|
||
isadm = user_tests.isadm()
|
||
isdeconnecteur = user_tests.isdeconnecteur()
|
||
ann_scol = config.ann_scol
|
||
script_utilisateur = user_tests.getuser()
|
||
|
||
##################################################################################
|
||
### Fonctions utiles
|
||
def decode(s):
|
||
""" Retourne un unicode à partir de s
|
||
s doit être en utf-8 """
|
||
return s.decode('utf-8','ignore') # On ignore les erreurs
|
||
|
||
accents = "êëèéÉÈÀÙâäàûüôöÖÔîïÎÏ'çÇÿßæÆøØ" # Si modif ici modifier aussi la fonction
|
||
def strip_accents(a):
|
||
""" Supression des accents de la chaîne fournie """
|
||
a = a.replace(u'ê','e').replace(u'ë','e').replace(u'è','e').replace(u'é','e').replace(u'É','e').replace(u'È','e')
|
||
a = a.replace(u'â','a').replace(u'ä','a').replace(u'à','a').replace(u'À','a')
|
||
a = a.replace(u'û','u').replace(u'ü','u').replace(u'ù','u').replace(u'Ù','u')
|
||
a = a.replace(u'ô','o').replace(u'ö','o').replace(u'Ö','o').replace(u'Ô','o')
|
||
a = a.replace(u'î','i').replace(u'ï','i').replace(u'Ï','i').replace(u'Î','i')
|
||
a = a.replace(' ','_').replace(u"'",'').replace(u'ç','c').replace(u'Ç','c')
|
||
a = a.replace(u'ÿ','y').replace(u'ß','ss').replace(u'æ','ae').replace(u'Æ','ae').replace(u'ø','o').replace(u'Ø','o')
|
||
return a
|
||
|
||
def mailexist(mail):
|
||
""" Vérifie si un mail existe ou non
|
||
grace à la commande vrfy du serveur mail """
|
||
try:
|
||
s=smtplib.SMTP(smtpserv)
|
||
r = s.vrfy(mail)
|
||
s.close()
|
||
except:
|
||
raise ValueError(u'Serveur de mail injoignable')
|
||
|
||
if r[0] in [250, 252]:
|
||
return True
|
||
else:
|
||
return False
|
||
|
||
def preattr(val):
|
||
"""
|
||
val est :
|
||
* un entier
|
||
* une chaîne
|
||
* une liste avec un seul entier ou chaine
|
||
|
||
Retourne
|
||
[ len(str(val).strip), str(val).strip en utf-8 ]
|
||
"""
|
||
|
||
t = type(val)
|
||
|
||
if t==list and len(val)==1:
|
||
return preattr(val[0])
|
||
|
||
elif t==str or t==int:
|
||
val = str(val).strip()
|
||
# On passe tout en utf-8 pour ne pas avoir de problèmes
|
||
# d'accents dans la base
|
||
return [ len(val) , unicode(val,'iso-8859-1').encode('utf-8') ]
|
||
elif t==unicode:
|
||
val = val.strip()
|
||
return [ len(val) , val.encode('utf-8') ]
|
||
else:
|
||
raise TypeError
|
||
|
||
|
||
def is_actif(sanction):
|
||
"""
|
||
Retourne True ou False suivant si la sanction founie (chaine venant de blacklist)
|
||
est active ou non
|
||
"""
|
||
bl = sanction.split(',')
|
||
try:
|
||
now = time.time()
|
||
debut = time.mktime( time.strptime(bl[0],date_format) )
|
||
if bl[1]=='-':
|
||
fin = now + 1
|
||
else:
|
||
fin = time.mktime( time.strptime(bl[1],date_format) )
|
||
return debut < now and fin > now
|
||
except:
|
||
return False
|
||
|
||
def format_mac(mac):
|
||
"""
|
||
Formatage des adresses mac
|
||
Transforme une adresse pour obtenir la forme xx:xx:xx:xx:xx:xx
|
||
Le séparateur original peut être :, - ou rien
|
||
Retourne la mac formatée.
|
||
"""
|
||
l, mac = preattr(mac)
|
||
mac = mac.strip().replace("-",":")
|
||
if mac.count(":") == 5:
|
||
# On a une adresse de la forme 0:01:02:18:d1:90
|
||
# On va compléter s'il manque des 0
|
||
mac = ":".join(map(lambda x: x.replace(' ', '0'),
|
||
map(lambda x: "%02s" % x, mac.split(":"))))
|
||
mac= mac.replace(':','').lower()
|
||
if len(mac)!=12:
|
||
raise ValueError(u'Longueur adresse mac incorrecte.')
|
||
for c in mac[:]:
|
||
if c not in string.hexdigits:
|
||
raise ValueError(u"Caractère interdit '%s' dans adresse mac." % c)
|
||
if mac=='000000000000':
|
||
raise ValueError(u"MAC nulle interdite\nIl doit être possible de modifier l'adresse de la carte.")
|
||
|
||
# Formatage
|
||
mac="%s:%s:%s:%s:%s:%s" % ( mac[:2],mac[2:4],mac[4:6], mac[6:8], mac[8:10], mac[10:] )
|
||
|
||
return mac
|
||
|
||
##################################################################################
|
||
### Définition des classes
|
||
|
||
class service:
|
||
""" Défini un service à redémarrer """
|
||
def __init__(self,nom,args=[],start=[]):
|
||
""" nom du service
|
||
liste des arguments
|
||
liste d'horaires de démarages """
|
||
self.nom=nom
|
||
self.args=args
|
||
self.start=map(int,start)
|
||
|
||
def __str__(self):
|
||
return "%s(%s) à partir du %s" % (self.nom, \
|
||
','.join(self.args), \
|
||
' et '.join(map(lambda t:time.strftime(date_format,time.gmtime(t)), self.start)))
|
||
|
||
class crans_ldap:
|
||
"""
|
||
Classe de connexion à la base LDAP du crans.
|
||
"""
|
||
conn=None
|
||
base_dn='ou=data,dc=crans,dc=org'
|
||
base_lock = 'ou=lock,dc=crans,dc=org'
|
||
base_services = 'ou=services,dc=crans,dc=org'
|
||
|
||
### Configuration de la recheche
|
||
# Dictionnaire de tranformation des champs
|
||
trans = { 'prénom': 'prenom' ,
|
||
'chambre': 'chbre',
|
||
'login': 'mail' ,
|
||
'hostname': 'host',
|
||
'mac': 'macAddress',
|
||
'ip': 'ipHostNumber' ,
|
||
'telephone': 'tel' }
|
||
|
||
# Champs de recherche pour la recherche automatique
|
||
auto_search_champs = { 'adherent': [ 'nom', 'prenom', 'tel', 'mail', 'chbre', 'mailAlias', 'cannonicalAlias' ], \
|
||
'machine': [ 'macAddress', 'host', 'ipHostNumber', 'hostAlias'] ,
|
||
'club': [ 'nom', 'chbre' ] }
|
||
|
||
# Champs de recherche pour la recherche manuelle (en plus de la recherche auto)
|
||
non_auto_search_champs = { 'adherent': [ 'etudes', 'paiement', 'carteEtudiant', 'aid' , 'postalAddress', 'historique' ,'blacklist', 'droits', 'uidNumber', 'uid', 'info', 'solde' , 'controle' ], \
|
||
'machine': [ 'mid' , 'ipsec', 'historique', 'blacklist' , 'puissance', 'canal', 'portTCPin', 'portTCPout', 'portUDPin', 'portUDPout', 'prise' , 'info', 'exempt' ] ,
|
||
'club': [ 'cid' , 'responsable', 'paiement', 'historique', 'blacklist', 'mailAlias', 'info', 'controle' ] }
|
||
|
||
# Scope des différentes recherches
|
||
scope = { 'adherent': 1 , 'machine': 2 , 'club': 1 }
|
||
|
||
def __init__(self):
|
||
self.connect()
|
||
|
||
def __del__(self):
|
||
# Destruction des locks résiduels
|
||
if hasattr(self,'_locks'):
|
||
for lock in self._locks:
|
||
self.remove_lock(lock)
|
||
|
||
def connect(self):
|
||
""" Initialisation des connexion vers le serveur LDAP """
|
||
|
||
def bind(conn,anon_bind=0):
|
||
""" Authentification auprès du serveur ldap """
|
||
nbessais = 0
|
||
ok = False
|
||
while not ok:
|
||
try:
|
||
if anon_bind:
|
||
conn.bind_s('','',ldap.AUTH_SIMPLE)
|
||
else:
|
||
conn.bind_s(ldap_auth_dn,ldap_password,ldap.AUTH_SIMPLE)
|
||
ok = True
|
||
|
||
except ldap.SERVER_DOWN:
|
||
nbessais += 1
|
||
if nbessais > 2:
|
||
sys.stderr.write("ERREUR : serveur LDAP injoignable\n")
|
||
sys.exit(1)
|
||
else:
|
||
sleep(0.3)
|
||
|
||
def select_conn(methode_base, methode_alternative):
|
||
""" Retoune une fonction qui :
|
||
1) bind sur la connection self.conn si necessaire
|
||
2) fait ce que ferai methode_base
|
||
3) si échoue bind sur la connexion self.rw_conn
|
||
4) fait ce que ferai methode_alternative """
|
||
|
||
def new_methode(*args):
|
||
try:
|
||
if not self.__conn_binded:
|
||
bind(self.conn,anon_bind)
|
||
self.__conn_binded = True
|
||
return methode_base(*args)
|
||
except ldap.STRONG_AUTH_REQUIRED:
|
||
# On a pas les droits necessaires ici
|
||
if not self.__rw_conn_binded:
|
||
bind(self.rw_conn)
|
||
self.__rw_conn_binded = True
|
||
return methode_alternative(*args)
|
||
|
||
return new_methode
|
||
|
||
# Les objets ldap necessaires
|
||
self.conn = ldap.initialize(uri)
|
||
self.__conn_binded = False
|
||
|
||
self.rw_conn = ldap.initialize(rw_uri)
|
||
self.__rw_conn_binded = False
|
||
|
||
# Modification des méthodes utilisées
|
||
self.conn.search_s = select_conn(self.conn.search_s,self.rw_conn.search_s)
|
||
self.conn.add_s = select_conn(self.conn.add_s,self.rw_conn.add_s)
|
||
self.conn.modify_s = select_conn(self.conn.modify_s,self.rw_conn.modify_s)
|
||
self.conn.delete_s = select_conn(self.conn.delete_s,self.rw_conn.delete_s)
|
||
|
||
def exist(self,arg):
|
||
"""
|
||
Vérifie l'existence d'une entrée dans la base et que cette entrée
|
||
n'appartient pas à l'objet en cours, prend en compte les locks
|
||
arg doit être une expression de recherche ldap
|
||
Si existence, retourne la liste de dn correspondants
|
||
Sinon retourne une liste vide
|
||
Exemple : exist('chbre=Z345') vérifie si il y a un adhérent en Z345
|
||
"""
|
||
r=[]
|
||
|
||
# Premier test: dans les objets déja inscrits
|
||
ret = self.conn.search_s(self.base_dn,2,arg)
|
||
for res in ret:
|
||
# C'est peut être l'objet courant
|
||
try: # Si ce n'est pas une classe fille avec l'attribu dn => erreur
|
||
if res[0] == self.dn:
|
||
continue
|
||
except:
|
||
pass
|
||
r.append(res[0])
|
||
|
||
# Deuxième test : lock ?
|
||
ret = self.conn.search_s(self.base_lock,1,arg)
|
||
lockid = '%s-%s' % (hostname, os.getpid() )
|
||
for res in ret:
|
||
# Lock encore actif ?
|
||
l = res[1]['lockid'][0]
|
||
if l == lockid: continue
|
||
# C'est locké par un autre process que le notre
|
||
# il tourne encore ?
|
||
try:
|
||
if l.split('-')[0] == hostname and os.system('ps %s > /dev/null 2>&1' % l.split('-')[1] ):
|
||
# Il ne tourne plus
|
||
self.remove_lock(res[0]) # delock
|
||
continue
|
||
except:
|
||
pass
|
||
r.append(res[0])
|
||
|
||
return r
|
||
|
||
def lock(self,item,valeur):
|
||
""" Lock un item avec la valeur valeur, les items possibles peuvent être :
|
||
aid $ chbre $ mail $ mailAlias $ cannonicalAlias $
|
||
mid $ macAddress $ host $ hostAlias $ ipHostNumber
|
||
retourne le dn du lock
|
||
"""
|
||
|
||
valeur = valeur.encode('utf-8')
|
||
|
||
if not valeur:
|
||
# On le lock pas un truc vide
|
||
return True
|
||
|
||
lock_dn = '%s=%s,%s' % ( item, valeur, self.base_lock )
|
||
lockid = '%s-%s' % (hostname, os.getpid() )
|
||
modlist = ldap.modlist.addModlist({ 'objectClass' : 'lock' ,
|
||
'lockid': lockid ,
|
||
item: valeur } )
|
||
|
||
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.remove_lock(res[0]) # delock
|
||
return self.lock(item,valeur) # relock
|
||
raise EnvironmentError(u'Objet (%s=%s) locké, patienter.' % (item, valeur), l)
|
||
else:
|
||
if not hasattr(self,'_locks'):
|
||
self._locks = [lock_dn]
|
||
else:
|
||
self._locks.append(lock_dn)
|
||
|
||
def remove_lock(self,lockdn):
|
||
""" Destruction d'un lock
|
||
Destruction de tous les locks si lockdn=*"""
|
||
# Mettre des verifs ?
|
||
if lockdn!='*':
|
||
self.conn.delete_s(lockdn)
|
||
try:
|
||
self._locks.remove(lockdn)
|
||
except:
|
||
# Pas grave si ca foire, le lock n'y est plus
|
||
pass
|
||
else:
|
||
locks = self.list_locks()
|
||
for l in locks:
|
||
self.conn.delete_s(l[0])
|
||
|
||
def list_locks(self):
|
||
""" Liste les locks """
|
||
return self.conn.search_s(self.base_lock,1,'objectClass=lock')
|
||
|
||
def services_to_restart(self,new=None,args=[],start=0):
|
||
""" Si new = None retourne la liste des services à redémarrer
|
||
Si new est fourni et ne commence pas par - ajoute le service à la liste
|
||
avec les arguments args (args doit être une liste).
|
||
start indique la date (secondes depuis epoch) à partir du moment ou
|
||
cette action doit être effectué
|
||
Si new commence par - supprime le service si son start est plus vieux que maintenant
|
||
Si new commence par -- supprime dans condition.
|
||
"""
|
||
if new: new = preattr(new)[1]
|
||
|
||
# Quels services sont déjà à redémarrer ?
|
||
serv = {} # { service: [ arguments ] }
|
||
serv_dates = {} # { service: [ dates de restart ] }
|
||
services = []
|
||
for s in self.conn.search_s(self.base_services,1,'objectClass=service'):
|
||
s=s[1]
|
||
serv[s['cn'][0]] = s.get('args',[])
|
||
serv_dates[s['cn'][0]] = s.get('start',[])
|
||
services.append(service(s['cn'][0],s.get('args',[]),s.get('start',[])))
|
||
|
||
# Retourne la liste des services à redémarrer
|
||
if not new: return services
|
||
|
||
# Effacement d'un service
|
||
if new[0] == '-':
|
||
if new[1] == '-':
|
||
# Double -- on enlève quelque soit la date
|
||
remove_dn='cn=%s,%s' % ( new[2:], self.base_services )
|
||
else:
|
||
# On enlève uniquement si la date est passée
|
||
remove_dn='cn=%s,%s' % ( new[1:], self.base_services )
|
||
if not serv.has_key(new[1:]):
|
||
# Existe pas => rien à faire
|
||
return
|
||
keep_date=[]
|
||
for date in serv_dates[new[1:]]:
|
||
if time.time() < int(date):
|
||
keep_date.append(date)
|
||
if keep_date:
|
||
self.conn.modify_s(remove_dn,ldap.modlist.modifyModlist({'start': serv_dates[new[1:]]}, { 'start': keep_date }))
|
||
remove_dn=None
|
||
|
||
if remove_dn:
|
||
# Supression
|
||
try: self.conn.delete_s(remove_dn)
|
||
except: pass
|
||
# Si n'existe pas => Erreur mais le résultat est la.
|
||
return
|
||
|
||
serv_dn = 'cn=%s,%s' % ( new, self.base_services )
|
||
|
||
# Petite fonction à appliquer aux arguments
|
||
if type(args) == str: args = [ args ]
|
||
args=map(lambda x:preattr(x)[1] ,args)
|
||
if type(start) == int: start = [ start ]
|
||
start=map(lambda x:preattr(x)[1] ,start)
|
||
|
||
if new in serv.keys():
|
||
modlist = []
|
||
|
||
new_args = []
|
||
# Nouveaux arguments ?
|
||
for arg in args:
|
||
if arg not in serv[new]:
|
||
new_args.append(arg)
|
||
if new_args:
|
||
modlist += ldap.modlist.modifyModlist({ 'args': serv[new]}, { 'args': serv[new] + new_args })
|
||
|
||
new_date = []
|
||
# Nouvelle date ?
|
||
for date in start:
|
||
if date not in serv_dates[new]:
|
||
new_date.append(date)
|
||
if new_date:
|
||
modlist += ldap.modlist.modifyModlist({'start': serv_dates[new]}, { 'start': serv_dates[new] + new_date })
|
||
|
||
if modlist:
|
||
try:
|
||
self.conn.modify_s(serv_dn,modlist)
|
||
except ldap.TYPE_OR_VALUE_EXISTS:
|
||
# Pas grave
|
||
pass
|
||
# else rien à faire
|
||
else:
|
||
# Entrée non présente -> ajout
|
||
modlist = ldap.modlist.addModlist({ 'objectClass': 'service' ,
|
||
'cn': new ,
|
||
'args': args ,
|
||
'start': start } )
|
||
try:
|
||
self.conn.add_s(serv_dn,modlist)
|
||
except ldap.ALREADY_EXISTS:
|
||
# Existe déja => rien à faire
|
||
pass
|
||
|
||
def search(self,expression,mode=''):
|
||
"""
|
||
Recherche dans la base LDAP, expression est une chaîne :
|
||
une expression : champ1=expr1 champ2=expr2 champ3!=expr3....
|
||
soit un seul terme, dans ce cas cherche sur les champs de auto_search_champs
|
||
Si mode ='w' les instances crées seront en mode d'écriture
|
||
"""
|
||
|
||
if type(expression)==str:
|
||
# Transformation de l'expression en utf-8
|
||
expression = unicode(expression,'iso-8859-15').encode('utf-8')
|
||
elif type(expression)==unicode:
|
||
expression = expression.encode('utf-8')
|
||
else:
|
||
raise TypeError(u'Chaine attendue')
|
||
|
||
if not expression:
|
||
return []
|
||
|
||
# Il faut un filtre par type d'objet de la base
|
||
filtres = self.auto_search_champs.keys()
|
||
result={'adherent': [], 'machine': [], 'club': []}
|
||
|
||
# Fonction utile
|
||
def build_filtre(champ,expr,neg=0):
|
||
""" Retourne une chaine pour recherche dans la base LDAP
|
||
du style (champ=expr) en adaptant les valeurs de expr au champ
|
||
si neg = 1: retourne le négatif : (!(champ=expr))"""
|
||
el = ''
|
||
if champ in [ 'host', 'hostAlias' ]:
|
||
if expr[-1] == '*':
|
||
el = '(%s=%s)' % (champ, expr)
|
||
elif expr.find('.')==-1:
|
||
el = '(%s=%s.*)' % ( champ, expr)
|
||
else:
|
||
el = '(%s=%s*)' % ( champ, expr)
|
||
elif champ == 'macAddress':
|
||
# Formatage adresse mac
|
||
try: el = '(macAddress=%s)' % format_mac(expr)
|
||
except: pass
|
||
elif champ == 'paiement' and expr == 'ok':
|
||
# Paiement donnant droit à une connexion maintenant ?
|
||
# il doit avoir payé pour
|
||
# l'année en cours ou pour l'année précédente
|
||
# si on est en septembre
|
||
#
|
||
# Dans tous les cas, pour un adhérent, le paiement est considéré non ok
|
||
# s'il n'a pas fourni sa carte d'etudiant alors que l'on est desormais
|
||
# en periode de bloquage definifif (cf config.py).
|
||
if localtime()[1] == 9:
|
||
# Pour septembre paiement année précédente ok
|
||
el = "(|(paiement=%s)(paiement=%s))" % (int(ann_scol), int(ann_scol)-1)
|
||
else:
|
||
el = "(paiement=%s)" % (int(ann_scol),)
|
||
# Doit-on bloquer en cas de manque de la carte d'etudiant ?
|
||
if config.bl_carte_et_definitif:
|
||
el = "(&(|(carteEtudiant=%s)(objectClass=club))%s)" % (int(ann_scol), el)
|
||
else:
|
||
# Cas général
|
||
el = '(%s=%s)' % (champ, expr)
|
||
if neg: el = '(!%s)' % el
|
||
return el
|
||
|
||
if expression.find('=')!=-1:
|
||
#### Recherche avec conditions explicites
|
||
## Construction des filtres
|
||
|
||
# initialisation
|
||
filtre={}
|
||
filtre_cond={} # Stokage si filtre avec condition (ne comporte pas que des *)
|
||
filtre_tout=1 # Passse à 0 si au moins une condition
|
||
|
||
for i in filtres:
|
||
filtre[i]='(&'
|
||
filtre_cond[i] = 0
|
||
conds = expression.split('&')
|
||
|
||
# Test de l'expression de recherche et classement par filtres
|
||
for cond in conds:
|
||
neg = 0
|
||
try:
|
||
champ, expr = cond.strip().split('=')
|
||
if champ[-1] == '!':
|
||
# Négation pour se champ
|
||
champ = champ[:-1]
|
||
neg = 1
|
||
except:
|
||
raise ValueError(u'Syntaxe de recherche invalide.')
|
||
|
||
# transformation de certains champs
|
||
champ = self.trans.get(champ,champ)
|
||
|
||
if expr=='': expr='*'
|
||
|
||
ok = 0
|
||
|
||
# Construction du filtre
|
||
for i in filtres:
|
||
if champ in self.auto_search_champs[i] + self.non_auto_search_champs[i]:
|
||
filtre[i] += build_filtre(champ,expr,neg)
|
||
ok = 1
|
||
if expr!='*' or neg:
|
||
filtre_cond[i] = 1
|
||
filtre_tout=0
|
||
if not ok:
|
||
raise ValueError(u'Champ de recherche inconnu (%s)'% champ )
|
||
|
||
## Recherche avec chacun des filtres
|
||
r={} # contiendra les réponses par filtre
|
||
for i in filtres:
|
||
if (filtre_tout and filtre[i]!='(&' ) or filtre_cond[i]:
|
||
# Filtre valide
|
||
#filtre[i] += ')'
|
||
filtre[i] += '(objectClass=%s))' % i
|
||
r[i] = self.conn.search_s(self.base_dn,self.scope[i],filtre[i])
|
||
else:
|
||
r[i] = None
|
||
|
||
## On a alors une liste de résultats
|
||
## r = { categorie1: [ (result1), (result2), ...] , ... }
|
||
|
||
# Traitement
|
||
if not r['machine'] and not r['adherent'] and not r['club']:
|
||
# Pas de réponses
|
||
return result
|
||
elif len(conds) == 1:
|
||
# Filtre sur un seul champ
|
||
# => on retourne tout
|
||
for i in filtres:
|
||
if not r[i]: continue
|
||
for res in r[i]:
|
||
result[i].append(globals()[i](res,mode,self.conn))
|
||
elif not r['adherent'] and not r['club']:
|
||
# Il n'y avait seulement un filtre machine
|
||
# => on retourne uniquement les machines trouvées
|
||
for m in r['machine']:
|
||
result['machine'].append(machine(m,mode,self.conn) )
|
||
elif not r['machine']:
|
||
# Il n'y avait pas de filtre machine
|
||
# => on retourne uniquement les adhérents
|
||
if r['adherent']:
|
||
for a in r['adherent']:
|
||
result['adherent'].append(adherent(a,mode,self.conn) )
|
||
if r['club']:
|
||
for a in r['club']:
|
||
result['club'].append(club(a,mode,self.conn) )
|
||
else:
|
||
# Il faut croiser les résultats machine et propriétaire
|
||
# Traitement des machines
|
||
mach_adh = [] # liste de dn d'adhérents et de clubs
|
||
for res in r['machine']:
|
||
dn = string.join(res[0].split(',')[-4:],',')
|
||
if dn[:3] != 'aid' and dn[:3] != 'cid': continue
|
||
if dn not in mach_adh:
|
||
mach_adh.append(dn)
|
||
|
||
# Croisement
|
||
bons_dn = [] # liste des dn d'adhérents qui correspondent aux critères
|
||
if r['adherent']:
|
||
for a in r['adherent']:
|
||
if a[0] in mach_adh and not a[0] in bons_dn:
|
||
bons_dn.append(a[0])
|
||
result['adherent'].append(adherent(a,mode,self.conn) )
|
||
if r['club']:
|
||
for a in r['club']:
|
||
if a[0] in mach_adh and not a[0] in bons_dn:
|
||
bons_dn.append(a[0])
|
||
result['club'].append(club(a,mode,self.conn) )
|
||
|
||
# Maintenant c'est au tour des bonnes machines
|
||
bons_dn2 = []
|
||
for a in r['machine']:
|
||
dn = string.join(a[0].split(',')[-4:],',')
|
||
if dn in bons_dn and not a[0] in bons_dn2:
|
||
bons_dn2.append(dn)
|
||
result['machine'].append(machine(a,mode,self.conn) )
|
||
|
||
else:
|
||
### Recherche d'une chaine sur tous les champs
|
||
conv = { 'machine': machine , 'club': club, 'adherent': adherent }
|
||
|
||
for i in filtres:
|
||
cl = conv[i]
|
||
|
||
# Construction du filtre
|
||
filtre = '(&(|'
|
||
for champ in self.auto_search_champs[i]:
|
||
filtre += build_filtre(champ,expression)
|
||
filtre+=')(objectClass=%s))' %i
|
||
|
||
# Recherche
|
||
for r in self.conn.search_s(self.base_dn,self.scope[i],filtre):
|
||
result[i].append( cl(r,mode,self.conn) )
|
||
|
||
return result
|
||
|
||
__machines = ()
|
||
def all_machines(self, graphic=False):
|
||
"""Renvoie toutes les machines autorisées.
|
||
|
||
Cela affiche des trucs et des bidules si graphic est à True."""
|
||
if graphic: from affich_tools import anim, cprint, OK
|
||
if not self.__machines:
|
||
# Récolte des données
|
||
if graphic: cprint('Lecture base LDAP','gras')
|
||
# Machines de l'assoce
|
||
self.__machines = crans(self.conn).machines()
|
||
# Machines des invités
|
||
self.__machines += invite(self.conn).machines()
|
||
# Machines des adhérents et clubs de l'année en cours
|
||
base = self.search('paiement=ok')
|
||
base = base['adherent'] + base['club']
|
||
if graphic: a = anim('\ttri machines',len(base))
|
||
for adh in base:
|
||
if graphic: a.cycle()
|
||
# Adhérent ayant payé l'année en cours
|
||
if 'bloq' in adh.blacklist_actif():
|
||
# Adhérent ignoré
|
||
continue
|
||
self.__machines += adh.machines()
|
||
if graphic: a.reinit()
|
||
if graphic: print OK
|
||
|
||
return self.__machines
|
||
|
||
#############################################################################
|
||
|
||
class base_classes_crans(crans_ldap):
|
||
""" Méthodes de base des classes machines, et base_proprietaire """
|
||
|
||
def __eq__(self, autre):
|
||
""" Test d'égalité de deux instances de club/adhérent/machine,
|
||
retourne True s'il s'agit du même club/adhérent/machine, False sinon """
|
||
return self.__class__==autre.__class__ and self.id()==autre.id()
|
||
|
||
def id(self):
|
||
""" Retourne la valeur de l'attribu caractéristique de la classe (aid,mid,cid)"""
|
||
try:
|
||
s = self.dn.split(',')[0].split('=')
|
||
if s[0] == self.idn:
|
||
return s[1]
|
||
except:
|
||
return ''
|
||
|
||
def blacklist_actif(self):
|
||
"""
|
||
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):
|
||
"""
|
||
Vérifie si l'instance courante est blacklistée ou a été blacklistée.
|
||
Retourne les sanctions en cours sous forme de dictionnaire avec comme clef
|
||
la sanction et comme valeur une liste de couple de dates correspondant aux
|
||
différentes périodes de sanctions.
|
||
|
||
ex: { 'upload' : (('17/11/2004 00:00','20/11/2004 00:00'), ('...', '...')) }
|
||
"""
|
||
bl_liste = self._data.get('blacklist',[])
|
||
|
||
if 'machine' in self._data['objectClass']:
|
||
# Il faut aussi regarder la blackliste du propriétaire
|
||
p = self.proprietaire()
|
||
bl_liste += p.blacklist()
|
||
|
||
actifs = {}
|
||
inactifs = {}
|
||
|
||
for sanction in bl_liste:
|
||
s = sanction.split(',')[2]
|
||
if is_actif(sanction):
|
||
if not s in actifs:
|
||
actifs[s] = []
|
||
actifs[s].append((sanction.split(',')[0],
|
||
sanction.split(',')[1]))
|
||
else:
|
||
if not s in inactifs:
|
||
inactifs[s] = []
|
||
inactifs[s].append((sanction.split(',')[0],
|
||
sanction.split(',')[1]))
|
||
return (actifs, inactifs)
|
||
|
||
def blacklist(self,new=None):
|
||
"""
|
||
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 doivent être sous la forme donnée par date_format
|
||
pour un début ou fin immédiate mettre now
|
||
pour une fin indéterminée mettre '-'
|
||
|
||
pour modifier une entrée donner un tuple de deux termes :
|
||
( index dans blacklist à modifier , nouvelle liste )
|
||
l'index est celui obtenu dans la liste retournée par blacklist()
|
||
"""
|
||
if not self._data.has_key('blacklist'):
|
||
self._data['blacklist']=[]
|
||
liste = list(self._data['blacklist'])
|
||
if new==None: return map(decode,liste)
|
||
|
||
if type(new)==tuple:
|
||
# Modif
|
||
index = new[0]
|
||
new = new[1]
|
||
if new=='':
|
||
liste.pop(index)
|
||
self._set('blacklist',liste)
|
||
return liste
|
||
else:
|
||
index = -1
|
||
|
||
if type(new)!=list or len(new)!=4:
|
||
raise TypeError
|
||
|
||
# Verif que les dates sont OK
|
||
if new[0] == 'now':
|
||
new[0] = time.strftime(date_format)
|
||
debut=0
|
||
else:
|
||
try: debut=int(time.mktime(time.strptime(new[0],date_format)))
|
||
except: raise ValueError(u'Date de début blacklist invalide')
|
||
|
||
if new[1] == 'now':
|
||
new[1] = time.strftime(date_format)
|
||
fin=0
|
||
elif new[1]!='-':
|
||
try: fin=int(time.mktime(time.strptime(new[1],date_format)))
|
||
except: raise ValueError(u'Date de fin blacklist invalide')
|
||
else:
|
||
fin = -1
|
||
|
||
if debut == fin:
|
||
raise ValueError(u'Dates de début et fin identiques')
|
||
|
||
# On d-Aépasse la fin de sanction d'1min pour être sur quelle périmé.-b
|
||
fin=fin+60
|
||
|
||
new_c = ','.join(new)
|
||
new_c = preattr(new_c)[1]
|
||
|
||
if index!=-1:
|
||
liste[index] = new_c
|
||
else:
|
||
liste = liste + [ new_c ]
|
||
|
||
if self._data['blacklist'] != liste:
|
||
self._data['blacklist']=liste
|
||
self.modifs.append('blacklist_' + new[2])
|
||
if not hasattr(self,"_blacklist_restart"):
|
||
self._blacklist_restart={}
|
||
if not self._blacklist_restart.has_key(new[2]):
|
||
self._blacklist_restart[new[2]] = [ debut, fin ]
|
||
else:
|
||
if debut not in self._blacklist_restart[new[2]]:
|
||
self._blacklist_restart[new[2]].append(debut)
|
||
if fin!=-1 and fin not in self._blacklist_restart[new[2]]:
|
||
self._blacklist_restart[new[2]].append(fin)
|
||
|
||
return liste
|
||
|
||
def restore(self):
|
||
""" Restore les données à l'état initial """
|
||
self._data = self._init_data.copy()
|
||
self.modifs=[]
|
||
|
||
def historique(self):
|
||
""" Retourne l'historique de l'objet """
|
||
return map(decode,self._data.get('historique',[]))
|
||
|
||
def info(self,new=None):
|
||
"""
|
||
Pour ajouter une remarque new doit être la chaîne
|
||
représentant la remarque à ajouter
|
||
Pour modifier new doit être une liste de la forme:
|
||
[ index de la remarque à modifier , nouvelle remarque ]
|
||
l'index est celui obtenu dans la liste retournée par info()
|
||
"""
|
||
if not self._data.has_key('info'):
|
||
self._data['info']=[]
|
||
liste = list(self._data['info'])
|
||
if new==None: return map(decode,liste)
|
||
|
||
if type(new)==list:
|
||
# Modif
|
||
index = new[0]
|
||
l, new = preattr(new[1])
|
||
if not new:
|
||
# Supression remarque
|
||
liste.pop(index)
|
||
else:
|
||
# Modif remarque
|
||
liste[index]=new
|
||
elif type(new)==str:
|
||
# Remarque supplémentaire
|
||
l, new = preattr(new)
|
||
if not new:
|
||
# On ajoute pas de remarque vide
|
||
return liste
|
||
# Ajout à la liste
|
||
liste = liste + [ new ]
|
||
else:
|
||
raise TypeError
|
||
|
||
self._set('info',liste)
|
||
return liste
|
||
|
||
def _save(self):
|
||
""" Sauvegarde dans la base LDAP """
|
||
if not self.modifs:
|
||
# Rien à faire
|
||
return []
|
||
|
||
if not self.dn:
|
||
# Enregistrement à placer en tête de base
|
||
self.dn = self.base_dn
|
||
|
||
# Construction de l'historique
|
||
if not self._init_data:
|
||
modif='inscription'
|
||
else:
|
||
### ON NE TOUCHE PAS A SELF.MODIFS, IL EST UTILISÉ PLUS LOIN !!!!!!!
|
||
modif=', '.join(self.modifs)
|
||
if "chbre" in self.modifs:
|
||
modif = modif.replace('chbre', "chbre %s -> %s" % (self._init_data["chbre"][0],
|
||
self._data["chbre"][0]))
|
||
|
||
if "solde" in self.modifs:
|
||
diff = float(self._init_data.get('solde',[0])[0]) - float(self._data.get('solde',[0])[0])
|
||
if diff > 0:
|
||
modif = modif.replace("solde", "debit %s Euros" % str(diff) )
|
||
else:
|
||
modif = modif.replace("solde", "credit %s Euros" % str(-diff) )
|
||
|
||
if 'droits' in self.modifs:
|
||
anciens_droits = self._init_data.get('droits',[])
|
||
nouveaux_droits = self._data.get('droits',[])
|
||
droits_ajoutes = ''.join( [ '+%s' % decode(d) for d in nouveaux_droits if d not in anciens_droits ] )
|
||
droits_enleves = ''.join( [ '-%s' % decode(d) for d in anciens_droits if d not in nouveaux_droits ] )
|
||
modif = modif.replace("droits", "droits : " + droits_ajoutes + droits_enleves )
|
||
|
||
timestamp = localtime()
|
||
hist = "%s, %s" % ( time.strftime(date_format, timestamp), script_utilisateur )
|
||
|
||
# On loggue
|
||
try:
|
||
fd = file('%s/%s_%s_%s' % ("%s/logs" % config.cimetiere, str(self.__class__).split('.')[-1],
|
||
time.strftime('%Y-%m-%d-%H:%M', timestamp), self.nom()),'wb')
|
||
fd.write("%s\n" % self._data)
|
||
fd.close()
|
||
except:
|
||
pass
|
||
|
||
# Suffit-t-il d'ajouter un item au dernier élément de l'historique ?
|
||
try:
|
||
dern = self._data['historique'][-1].split(': ',2)
|
||
if dern[0] == hist:
|
||
# Même date et même cableur
|
||
if modif not in dern[1].split(', '):
|
||
# Qqch de plus de modifié
|
||
self._data['historique'][-1] = self._data['historique'][-1] + ', ' +modif
|
||
else:
|
||
# Nouvelle entrée
|
||
# NE PAS UTILISER L'OPERATEUR += ICI sinon self._init_data aussi modififié
|
||
self._data['historique'] = self._data['historique'] + [ preattr("%s : %s" % ( hist, modif ))[1] ]
|
||
except:
|
||
# Nouvelle inscription
|
||
self._data['historique'] = [ "%s : %s" % ( hist, modif ) ]
|
||
|
||
if not self._init_data:
|
||
### Nouvel enregistrement
|
||
# Génération du dn
|
||
res = self.conn.search_s(self.base_dn,2,'objectClass=%s' % self._data['objectClass'][0],[''])
|
||
vidn=1
|
||
vidns=[]
|
||
# Liste des dn pris
|
||
for r in res:
|
||
# r=( dn, {} )
|
||
r = r[0].split(',')[0]
|
||
if r[:4] != '%s=' % self.idn: continue
|
||
vidns.append(int(r[4:]))
|
||
# On prend le premier libre
|
||
while vidn in vidns:
|
||
vidn += 1
|
||
|
||
self.dn='%s=%s,%s' % (self.idn, vidn, self.dn)
|
||
self._data[self.idn]= [ '%d' % vidn ]
|
||
|
||
# Ecriture
|
||
modlist = ldap.modlist.addModlist(self._data)
|
||
self.conn.add_s(self.dn,modlist)
|
||
else:
|
||
### Modification entrée
|
||
if not self._modifiable:
|
||
raise RuntimeError(u'Objet non modifiable')
|
||
modlist = ldap.modlist.modifyModlist(self._init_data,self._data)
|
||
try:
|
||
self.conn.modify_s(self.dn,modlist)
|
||
except ldap.TYPE_OR_VALUE_EXISTS , c:
|
||
champ = c.args[0]['info'].split(':')[0]
|
||
raise RuntimeError(u'Entrée en double dans le champ %s' % champ)
|
||
|
||
### Génération de la liste de services à redémarrer
|
||
# Quasiement tout est traité dans les classes filles.
|
||
if hasattr(self,"_blacklist_restart"):
|
||
for n,t in self._blacklist_restart.items():
|
||
self.services_to_restart("blacklist_%s"%n,[],t)
|
||
|
||
# Reinitialisation
|
||
self._init_data = self._data.copy()
|
||
|
||
def _delete(self,dn,comment=''):
|
||
""" Sauvegarde puis destruction du dn (et des sous-dn) fourni """
|
||
# Commentaires
|
||
comment = preattr(comment)[1]
|
||
self.modifs.append('destruction (%s)' % comment)
|
||
self._save()
|
||
|
||
# Sauvegarde
|
||
t = str(self.__class__).split('.')[-1]
|
||
fd = open('%s/%s/%s_%s' % (config.cimetiere, t, time.strftime('%Y-%m-%d-%H:%M'), self.nom()),'wb')
|
||
self.conn = None # Fermeture des connexions à la base sinon cPickle ne marchera pas
|
||
self.rw_conn = None
|
||
cPickle.dump(self,fd,2)
|
||
fd.close()
|
||
index = u"%s, %s : %s %s # %s\n" % (time.strftime(date_format), script_utilisateur, t, self.Nom() , decode(comment))
|
||
|
||
self.connect() # Reconnexion à la base
|
||
# Destruction
|
||
data = self.conn.search_s(dn,2)
|
||
|
||
data.reverse() # Necessaire pour détruire d'abord les sous-dn
|
||
for r in data:
|
||
self.conn.delete_s(r[0])
|
||
|
||
try:
|
||
log = open(config.cimetiere + '/index','a')
|
||
log.write(index)
|
||
log.close()
|
||
except:
|
||
pass
|
||
|
||
def _set(self,champ,val):
|
||
""" Met à jour les données de data et modifie modifs si besoin """
|
||
if (not self._data.has_key(champ) and val != []) \
|
||
or (self._data.has_key(champ) and self._data[champ]!=val):
|
||
self._data[champ]=val
|
||
if champ not in self.modifs:
|
||
self.modifs.append(champ)
|
||
|
||
#############################################################################
|
||
|
||
class base_proprietaire(base_classes_crans):
|
||
""" Méthodes de bases pour les classes adherent et club """
|
||
def __init__(self,data=(),mode='',conn=None):
|
||
"""
|
||
Si data est fourni initialise l'adhérent avec les valeurs données
|
||
Format de data : tuple comme retourné par une recherche dans la base ldap:
|
||
( dn, { donnée })
|
||
Si mode='w' : le propriétaire pourra être modifié
|
||
Attention, si mode ='w' mais si l'objet est déja locké il n'y a pas d'erreur
|
||
vérifier l'obtetion du lock grace à la valeur de _modifiable (si =w c'est bon)
|
||
Il est inutile de préciser le mode pour un nouveau proprietaire
|
||
|
||
conn est une instance de la classe de connexion à la base LDAP
|
||
"""
|
||
self.conn = conn
|
||
if not self.conn: self.connect()
|
||
|
||
if type(data) != tuple:
|
||
raise TypeError
|
||
|
||
self.modifs=[]
|
||
if data:
|
||
self.dn=data[0]
|
||
if mode == 'w':
|
||
try:
|
||
self.lock(self.idn, self.id())
|
||
self._modifiable = 'w'
|
||
except EnvironmentError , c:
|
||
self._modifiable = 0
|
||
else:
|
||
self._modifiable = 0
|
||
# Utile pour construire l'instruction LDAP de modif
|
||
self._init_data = data[1].copy()
|
||
self._data = data[1]
|
||
else:
|
||
# Propriétaire vide
|
||
self.dn='' # Génération du reste au moment de l'écriture
|
||
self._data={ 'objectClass': [ self.objectClass ] }
|
||
self._init_data={}
|
||
self._modifiable = 'w'
|
||
|
||
def chsh(self,new=None):
|
||
""" Retourne ou change le shell de l'adhérent """
|
||
if new == None:
|
||
try: return decode(self._data.get('loginShell',[''])[0])
|
||
except: return ''
|
||
else :
|
||
new = preattr(new)[1]
|
||
self._set('loginShell',[new])
|
||
return new
|
||
|
||
def alias(self,new=None):
|
||
"""
|
||
Création ou visualisation des alias mail
|
||
Même sytème d'argument que la méthode info.
|
||
"""
|
||
if not self._data.has_key('mailAlias'):
|
||
self._data['mailAlias']=[]
|
||
liste = list(self._data['mailAlias'])
|
||
if new==None:
|
||
return map(decode,liste)
|
||
|
||
if type(new)==list:
|
||
# Modif
|
||
index = new[0]
|
||
new = new[1]
|
||
if new=='':
|
||
# Supression alias
|
||
liste.pop(index)
|
||
self._set('mailAlias',liste)
|
||
return liste
|
||
else:
|
||
new = new.replace('@crans.org','')
|
||
index=-1
|
||
|
||
# Tests
|
||
l, new = preattr(new)
|
||
new = new.lower()
|
||
if l<2:
|
||
raise ValueError(u"Alias trop court.")
|
||
for c in new[:]:
|
||
if not c in (string.letters + string.digits + '-_.'):
|
||
raise ValueError(u"Alias : seuls les caractères alphanumériques, le -, le _ et le . sont autorisés." )
|
||
if new[0] not in string.letters:
|
||
raise ValueError(u"Le premier caractère de l'alias doit être alphabétique.")
|
||
if mailexist(new):
|
||
raise ValueError(u"Alias existant ou correspondand à un compte.")
|
||
|
||
if index!=-1:
|
||
liste[index]=new
|
||
else:
|
||
liste = liste + [ new ]
|
||
|
||
# Lock de mailAlias
|
||
self.lock('mailAlias',new)
|
||
|
||
self._set('mailAlias',liste)
|
||
return liste
|
||
|
||
def machines(self):
|
||
""" Retourne les machines (instances) appartenant à la classe """
|
||
if self.id():
|
||
res = []
|
||
try:
|
||
for r in self.conn.search_s('%s=%s,%s' % ( self.idn,self.id() , self.base_dn ),1,'objectClass=machine'):
|
||
res.append(machine(r, self._modifiable,self.conn) )
|
||
return res
|
||
except:
|
||
return []
|
||
else:
|
||
return []
|
||
|
||
def machines_fixes(self):
|
||
""" Retourne les machines fixes appartenant à l'instance """
|
||
return filter(lambda x: not x.ipsec(), self.machines())
|
||
|
||
def machines_wifi(self):
|
||
""" Retourne les machines wifi appartenant à l'instance """
|
||
return filter(lambda x: x.ipsec(), self.machines())
|
||
|
||
def solde(self, operation=None):
|
||
""" Retourne ou modifie le solde d'un propriétaire
|
||
operation doit être un nombre positif ou négatif
|
||
(string ou int ou float)
|
||
"""
|
||
solde = float(self._data.get('solde',[0])[0])
|
||
|
||
if operation==None:
|
||
return solde
|
||
|
||
# On effectue une opération
|
||
try:
|
||
new = solde + float(str(operation).replace(',','.'))
|
||
except ValueError:
|
||
raise ValueError(u"Il faut donner un nombre en argument.")
|
||
|
||
# découvert accepté
|
||
if new < config.impression.decouvert:
|
||
raise ValueError(u"Solde minimal atteind, opération non effectuée.")
|
||
|
||
self._set('solde',[str(new)])
|
||
return new
|
||
|
||
def controle(self,new=None):
|
||
"""
|
||
Controle du tresorier
|
||
New est de la forme {+-}{pc}
|
||
(p pour le paiement, c pour la carte
|
||
Retourne une chaine contenant p ou c ou les deux
|
||
"""
|
||
actuel = self._data.get('controle',[''])[0]
|
||
if new==None:
|
||
return actuel
|
||
|
||
if not sre.match(r'^[+-][pc]$', new):
|
||
raise ValueError('modification de controle incorrecte')
|
||
|
||
for c in 'pc':
|
||
if new == '+%s' % c and c not in actuel:
|
||
actuel += c
|
||
if new == '-%s' % c:
|
||
actuel = actuel.replace(c, '')
|
||
|
||
if actuel == '': self._set('controle',[])
|
||
else: self._set('controle',[actuel])
|
||
return actuel
|
||
|
||
def paiement(self,action=None):
|
||
"""
|
||
Action est un entier représentant une année
|
||
si positif ajoute l'année à la liste
|
||
si négatif le supprime
|
||
"""
|
||
return self._an('paiement',action)
|
||
|
||
def delete(self,comment=''):
|
||
"""Destruction du proprietaire"""
|
||
|
||
for m in self.machines():
|
||
# Destruction machines
|
||
m.delete(comment)
|
||
|
||
self._delete(self.dn,comment)
|
||
|
||
try:
|
||
if self.compte():
|
||
args = self._data['uid'][0] + ','
|
||
args+= self._data['homeDirectory'][0]
|
||
self.services_to_restart('del_user',[ args ] )
|
||
except:
|
||
# Si ne peux avoir de compte
|
||
pass
|
||
|
||
def save(self):
|
||
"""
|
||
Enregistre l'adhérent ou le club courant dans la base LDAP
|
||
Ajoute le mail de bienvenue à la liste des services à redémarrer
|
||
Retourne une chaîne indiquant les opération effectuées.
|
||
"""
|
||
# Note: un peu trop de fonctions pour un club mais ce n'est pas génant
|
||
|
||
ret =''
|
||
if self._init_data:
|
||
nouveau =0
|
||
# Reconfiguration switch si changement de chambre et si machine fixe
|
||
if 'chbre' in self.modifs:
|
||
for m in self.machines():
|
||
if not m.ipsec():
|
||
self.services_to_restart('switch',[self._data['chbre'][0]])
|
||
self.services_to_restart('switch',[self._init_data.get('chbre','')[0]])
|
||
break
|
||
else:
|
||
nouveau = 1
|
||
|
||
if 'chbre' in self.modifs and '????' in [ self._init_data.get("chbre",[''])[0] , self._init_data.get("chbre",[''])[0] ]:
|
||
self.services_to_restart('bl_chbre_invalide')
|
||
|
||
# Enregistrement
|
||
self._save()
|
||
|
||
# Message de sortie
|
||
if nouveau:
|
||
ret += coul(u"%s inscrit avec succès." % self.Nom(), 'vert')
|
||
|
||
if self.idn !='cid' and self.etudes(1) != "Pers":
|
||
# Mail de bienvenue
|
||
self.services_to_restart('mail_bienvenue',[self.mail().encode('iso-8859-15')])
|
||
|
||
else:
|
||
ret += coul(u"Modification %s effectuée avec succès." % self.Nom(), 'vert')
|
||
|
||
# Changements administratifs
|
||
test_carte = 'carteEtudiant+%s' % ann_scol in self.modifs \
|
||
or 'carteEtudiant-%s' % ann_scol in self.modifs
|
||
|
||
if test_carte and self.machines():
|
||
self.services_to_restart('bl_carte_etudiant')
|
||
|
||
if 'paiement+%s' % ann_scol in self.modifs \
|
||
or 'paiement-%s' % ann_scol in self.modifs \
|
||
or ( config.bl_carte_et_definitif and test_carte ):
|
||
for m in self.machines():
|
||
self.services_to_restart('macip',[m.ip()] )
|
||
self.services_to_restart('dns')
|
||
if m.ipsec():
|
||
self.services_to_restart('conf_wifi_ng')
|
||
self.services_to_restart('nectaris-dhcp')
|
||
else:
|
||
self.services_to_restart('switch',[self.chbre()])
|
||
self.services_to_restart('rouge-dhcp')
|
||
|
||
# Vérification si changement de bât, ce qui obligerai un changement d'IP
|
||
if 'chbre' in self.modifs and self.chbre()!='????':
|
||
# Verif si machines avec bonnes ip
|
||
err = 0
|
||
for m in self.machines():
|
||
if m.ipsec():
|
||
# Machine Wifi
|
||
continue
|
||
# Machine fixe
|
||
ip = m.ip()
|
||
try:
|
||
# Tentative de changement d'IP de la machine
|
||
m.ip(ip)
|
||
except ValueError:
|
||
# IP invalide, on la change
|
||
ret += "\nChangement d'IP machine %s : " % m.nom()
|
||
try:
|
||
ret += "%s -> %s" % ( ip, m.ip('<automatique>') )
|
||
m.save()
|
||
except Exception, c:
|
||
ret += coul(u'ERREUR : %s' % c.args[0], rouge)
|
||
err = 1
|
||
|
||
if err: ret += '\nEssayer de corriger les erreurs machines en éditant celles-ci.\n'
|
||
|
||
# Faut-il créer un compte sur vert ?
|
||
if 'compte' in self.modifs:
|
||
ret += u'\nUn compte a été créé :\n login : %s\n' % self.compte()
|
||
args = self._data['homeDirectory'][0] + ','
|
||
args+= self._data['uidNumber'][0] + ','
|
||
args+= self._data['uid'][0]
|
||
self.services_to_restart('home',[ args ])
|
||
r = prompt(u"Attribuer tout de suite un mot de passe ? [O/N]","O")
|
||
if r=='O' or r=='o':
|
||
chgpass(self.dn)
|
||
else:
|
||
ret += coul(u' Il faudra penser à attribuer un mot de passe\n','jaune')
|
||
|
||
# Modif des droits ?
|
||
if 'droits' in self.modifs:
|
||
self.services_to_restart('droits')
|
||
self.services_to_restart('mail_modif',['uid=%s' % self._data['uid'][0]])
|
||
|
||
# Remise à zero
|
||
self.modifs=[]
|
||
|
||
return ret
|
||
|
||
def _an(self,champ,action):
|
||
"""
|
||
Champ est un champ contenant une liste d'entiers
|
||
Action est un entier représentant une année
|
||
si positif ajoute l'année à la liste
|
||
si négatif le supprime
|
||
"""
|
||
if not self._data.has_key(champ):
|
||
trans=[]
|
||
else:
|
||
# On va travailler sur une liste d'entiers
|
||
trans = map(int,self._data[champ])
|
||
|
||
if action==None:
|
||
return trans
|
||
|
||
if type(action)!=int: raise TypeError
|
||
|
||
if action>0 and action not in trans:
|
||
trans.append(action)
|
||
act = '%s+%d' % (champ,action)
|
||
if act not in self.modifs:
|
||
self.modifs.append(act)
|
||
elif action<0 and -action in trans:
|
||
trans.remove(-action)
|
||
act = '%s%s' % (champ,action)
|
||
if act not in self.modifs:
|
||
self.modifs.append(act)
|
||
|
||
trans.sort()
|
||
new=[]
|
||
for an in trans:
|
||
new.append('%d' % an )
|
||
|
||
self._data[champ] = new
|
||
|
||
return self._data[champ]
|
||
|
||
#############################################################################
|
||
|
||
class adherent(base_proprietaire):
|
||
""" Classe de définition d'un adhérent """
|
||
objectClass = 'adherent'
|
||
idn = 'aid'
|
||
|
||
### Méthodes Nom utilisée lors de l'affichage des propriétés
|
||
### (commune avec les classes crans et club)
|
||
def Nom(self):
|
||
""" Retourne prenom nom """
|
||
return "%s %s" % ( self.prenom() , self.nom() )
|
||
|
||
def nom(self,new=None):
|
||
return self.__nom_prenom('nom',new)
|
||
|
||
def prenom(self,new=None):
|
||
return self.__nom_prenom('prenom',new)
|
||
|
||
def __nom_prenom(self,champ,new):
|
||
if new==None:
|
||
return decode(self._data.get(champ,[''])[0])
|
||
|
||
l, new = preattr(new)
|
||
new = new.capitalize()
|
||
for c in new[:]:
|
||
if c not in (string.letters + '- ' + preattr(accents)[1] ):
|
||
raise ValueError(u"Seuls les caractères alphabétiques, l'espace et le - sont permis dans %s." % champ.replace(u'e',u'é') )
|
||
if l<2:
|
||
raise ValueError(u"%s trop court." % champ.capitalize().replace(u'e',u'é'))
|
||
if new[0] not in string.letters:
|
||
raise ValueError(u"Le premier caractère du %s doit être une lettre" % champ.replace(u'e',u'é') )
|
||
|
||
self._set(champ,[new])
|
||
return new
|
||
|
||
def tel(self,new=None):
|
||
if new==None:
|
||
return self._data.get('tel',[''])[0]
|
||
|
||
if new != 'inconnu':
|
||
l, new = preattr(new)
|
||
if not new.isdigit() or l<6 or l>15:
|
||
raise ValueError(u"Numéro de téléphone incorrect (il doit comporter uniquement des chiffres).")
|
||
|
||
self._set('tel',[new])
|
||
return new
|
||
|
||
def chbre(self,new=None):
|
||
"""
|
||
Défini la chambre d'un adhérent, EXT pour personne extérieure au campus
|
||
"""
|
||
if new==None:
|
||
return decode(self._data.get('chbre',[''])[0])
|
||
|
||
l, new = preattr(new)
|
||
if l==0:
|
||
raise ValueError(u"Chambre incorrecte.")
|
||
|
||
if new.upper() == 'EXT':
|
||
# N'est pas ou plus sur le campus
|
||
# Machine fixe ?
|
||
# for m in self.machines():
|
||
# if not m.ipsec():
|
||
# raise ValueError(u'Un adhérent en dehors du campus ne doit pas avoir de machine fixe.')
|
||
|
||
self._set('chbre',['EXT'])
|
||
return 'EXT'
|
||
elif new.upper() == '????':
|
||
# On ne sait pas ou est l'adhérent
|
||
self._set('chbre',['????'])
|
||
return '????'
|
||
|
||
new = new.capitalize()
|
||
bat = new[0].lower()
|
||
|
||
if bat in annuaires.chbre_prises.keys():
|
||
# On a la liste des chambres
|
||
chbres = annuaires.chbre_prises[bat].keys()
|
||
if new[1:] not in chbres or len(new)<4 or not new[1:4].isdigit():
|
||
chbres.sort()
|
||
aide = u"Chambre inconnue dans le batiment, les chambres valides sont :"
|
||
a=0
|
||
for c in chbres:
|
||
if len(c)>=3 and not c[:3].isdigit():
|
||
# C'est un local club
|
||
continue
|
||
if int(a/14)>int((a-1)/14): aide += '\n '
|
||
if c[0]!='X':
|
||
aide += c.ljust(5)
|
||
a=a+1
|
||
aide += u'\n'
|
||
aide += u" " + annuaires.aide.get(bat,'')
|
||
raise ValueError(aide)
|
||
|
||
else:
|
||
raise ValueError(u'Bâtiment inconnu.')
|
||
|
||
# La chambre est valide, est-elle déja occupée ?
|
||
test = self.exist('chbre=%s' % new)
|
||
if test:
|
||
search = test[0].split(',')[0]
|
||
if search.split('=')[0]!='aid':
|
||
raise ValueError(u'Chambre déjà occupée.')
|
||
adh = self.search(search,self._modifiable)['adherent']
|
||
if len(adh) != 1:
|
||
raise ValueError(u'Chambre déjà occupée.')
|
||
else:
|
||
raise ValueError(u'Chambre déjà occupée.',adh[0])
|
||
|
||
# Lock de la chambre
|
||
self.lock('chbre',new)
|
||
|
||
self._set('chbre',[new])
|
||
return new
|
||
|
||
def adresse(self,new=None):
|
||
""" Défini l'adresse pour les personnes extérieures (dont la chambre = EXT)
|
||
L'adresse est une liste de 4 éléments : numero, rue, code postal, ville
|
||
"""
|
||
if new==None:
|
||
if self.chbre() != 'EXT':
|
||
# Personne sur le campus
|
||
return [u'',u'',u'',u'']
|
||
else:
|
||
addr = self._data.get('postalAddress', ['','','',''])[:4]
|
||
if len(addr) < 4:
|
||
addr = addr + ['']*(4-len(addr))
|
||
return map(decode,addr)
|
||
|
||
if type(new)!=list and len(new)!=4:
|
||
raise TypeError
|
||
|
||
l_min = [ 2, 0, 5, 2 ]
|
||
for i in range(0,4):
|
||
l, new[i] = preattr(new[i])
|
||
if l < l_min[i]: raise ValueError(u"Adresse incorrecte.")
|
||
|
||
# Correction si necessaire
|
||
if not new[1]:
|
||
new[1] = ' '
|
||
|
||
self._set('postalAddress',new)
|
||
return new
|
||
|
||
def mail(self,new=None):
|
||
if new==None:
|
||
return decode(self._data.get('mail',[''])[0])
|
||
|
||
l, new = preattr(new)
|
||
new = new.lower()
|
||
|
||
#Emplacement de l'@
|
||
a=new.find('@')
|
||
#Emplacement du . final
|
||
b=new.rfind('.')
|
||
|
||
# Les tests :
|
||
# exactement un @
|
||
# 2 ou 3 caractères après le . final
|
||
# @ pas en premier ni juste avant le dernier .
|
||
if new.count('@')!=1 \
|
||
or not ( l-b==4 or l-b==3) \
|
||
or a<1 or b-a<2:
|
||
raise ValueError(u"Adresse mail incorrecte.")
|
||
|
||
# Pas de caractèrs bizarres
|
||
for l in new[:]:
|
||
if not l in (string.lowercase + string.digits + '-_.@'):
|
||
raise ValueError(u"Caractère interdits dans l'adresse mail (%s)." % l)
|
||
|
||
# Pour les vicieux
|
||
if sre.match('.*crans.(org|ens-cachan.fr)$',new):
|
||
raise ValueError(u"Adresse mail @crans interdite ici")
|
||
|
||
# Il ne doit pas y avoir de compte
|
||
self.supprimer_compte()
|
||
self._set('mail',[new])
|
||
|
||
return new
|
||
|
||
def supprimer_compte(self):
|
||
u"""
|
||
Supprime le compte sur zamok. Penser à définir l'adresse mail après.
|
||
"""
|
||
self._set('mail', [''])
|
||
self._data['objectClass'] = [ 'adherent' ]
|
||
|
||
for c in [ 'uid', 'cn', 'shadowLastChange', 'shadowMax', 'shadowWarning', 'loginShell', 'userPassword', 'uidNumber', 'gidNumber', 'homeDirectory', 'gecos', 'droits','mailAlias', 'cannonicalAlias' ]:
|
||
try: self._data.pop(c)
|
||
except: pass
|
||
|
||
def etudes(self,index_or_new):
|
||
"""
|
||
Retourne l'un des 3 champs études (selon index_or_new si entier)
|
||
"""
|
||
if type(index_or_new)==int:
|
||
if self._data.has_key('etudes'):
|
||
return decode(self._data['etudes'][index_or_new])
|
||
else:
|
||
return ''
|
||
|
||
if type(index_or_new)!=list:
|
||
raise TypeError
|
||
|
||
if not self._data.has_key('etudes'):
|
||
self._data['etudes']=['','','']
|
||
|
||
# Pas grand chose à faire à part vérifier que ce sont bien des chaines
|
||
if len(index_or_new)!=3:
|
||
raise ValueError(u"Format études non valides.")
|
||
|
||
new = ['','','']
|
||
for i in range(0,3):
|
||
n = preattr(index_or_new[i])[1]
|
||
if n in new or n=='':
|
||
raise ValueError(u"Etudes non valides.")
|
||
new[i] = n
|
||
|
||
self._set('etudes',new)
|
||
|
||
return new
|
||
|
||
def carteEtudiant(self,action=None):
|
||
"""
|
||
Action est un entier représentant une année
|
||
si positif ajoute l'année à la liste
|
||
si négatif le supprime
|
||
"""
|
||
return self._an('carteEtudiant',action)
|
||
|
||
def checkPassword(self, password):
|
||
"""Vérifie le mot de passe de l'adhérent"""
|
||
anon = ldap.initialize(uri)
|
||
try:
|
||
r = anon.simple_bind(self.dn, password)
|
||
anon.result(r)
|
||
except ldap.INVALID_CREDENTIALS:
|
||
# A priori, le mot de passe est pas bon, il se peut aussi
|
||
# que l'utilisateur n'existe pas
|
||
return False
|
||
return True
|
||
|
||
|
||
def compte(self,login=None,uidNumber=0,hash_pass='',shell=config.login_shell):
|
||
"""
|
||
Création d'un compte à un adhérent
|
||
(la création se fait après l'écriture dans la base par la méthode save)
|
||
Si login = None, retourne le compte de l'adhérent
|
||
"""
|
||
if not login:
|
||
if self.mail().find('@') != -1:
|
||
return ''
|
||
else:
|
||
return self.mail()
|
||
|
||
# Supression des accents et espaces
|
||
login = strip_accents(login)
|
||
|
||
l, login = preattr(login)
|
||
login = login.lower()
|
||
|
||
if 'posixAccount' in self._data['objectClass']:
|
||
if login != self._data['uid']:
|
||
# A déja un compte
|
||
raise ValueError(u"L'adhérent à déjà un compte. Login : %s" % self._data['uid'][0])
|
||
else:
|
||
return login
|
||
|
||
for c in login[:]:
|
||
if not c in (string.letters + '-'):
|
||
raise ValueError(u"Seuls les caractères alphabétiques non accentués et le - sont autorisés dans le login.")
|
||
if l<2:
|
||
raise ValueError(u"Login trop court.")
|
||
if l>config.maxlen_login:
|
||
raise ValueError(u"Login trop long.")
|
||
if login[0]=='-':
|
||
raise ValueError(u"- interdit en première position.")
|
||
|
||
if mailexist(login):
|
||
raise ValueError(u"Login existant ou correspondant à un alias mail.",1)
|
||
|
||
home = '/home/' + login
|
||
if os.path.exists(home):
|
||
raise ValueError(u'Création du compte impossible : home existant',1)
|
||
|
||
# Lock du mail
|
||
self.lock('mail',login)
|
||
|
||
self._data['mail']= [ login ]
|
||
if not 'compte' in self.modifs:
|
||
self.modifs.append('compte')
|
||
|
||
# Création de l'alias cannonique
|
||
if self.nom() and self.prenom():
|
||
a = '%s.%s' % (self.prenom().capitalize(), self.nom().capitalize())
|
||
self.cannonical_alias(a)
|
||
|
||
self._data['objectClass'] = [ 'adherent', 'posixAccount', 'shadowAccount' ]
|
||
self._data['uid'] = [ login ]
|
||
self._data['cn'] = [ preattr(self.Nom())[1] ]
|
||
#self._data['shadowLastChange'] = [ '12632' ]
|
||
#self._data['shadowMax'] = [ '99999']
|
||
#self._data['shadowWarning'] = [ '7' ]
|
||
self._data['loginShell' ] = [ shell ]
|
||
if hash_pass:
|
||
self._data['userPassword'] = [ hash_pass ]
|
||
|
||
if uidNumber:
|
||
if self.exist('(uidNumber=%s)' % uidNumber):
|
||
raise ValueError(u'uidNumber pris')
|
||
else:
|
||
uidNumber = 1000
|
||
while self.exist('(uidNumber=%s)' % uidNumber):
|
||
uidNumber += 1
|
||
try:
|
||
self.lock('uidNumber',str(uidNumber))
|
||
except:
|
||
# Quelqu'un nous a piqué l'uid que l'on venait de choisir !
|
||
return self.compte(login,uidNumber,hash_pass,shell)
|
||
|
||
self._data['uidNumber']= [ str(uidNumber) ]
|
||
self._data['gidNumber']=[ str(config.gid) ]
|
||
|
||
self._data['homeDirectory']=[ preattr(home)[1] ]
|
||
self._data['gecos'] = [ preattr(strip_accents(self.Nom()))[1] + ',,,' ]
|
||
|
||
return decode(login)
|
||
|
||
def cannonical_alias(self,new=None):
|
||
""" Retourne ou défini l'alias canonique"""
|
||
if new == None:
|
||
try: return decode(self._data['cannonicalAlias'][0])
|
||
except: return ''
|
||
else :
|
||
a = strip_accents(new)
|
||
a = preattr(a)[1]
|
||
|
||
if not mailexist(a):
|
||
# Attribution de l'alias, sinon on passe
|
||
|
||
# Lock de cannonicalAlias
|
||
self.lock('cannonicalAlias',a)
|
||
|
||
# Attribution
|
||
self._set('cannonicalAlias',[a])
|
||
return a
|
||
|
||
def droits(self,droits=None):
|
||
""" droits est la liste des droits à donner à l'utilisateur """
|
||
if droits==None:
|
||
return map(decode,self._data.get('droits',[]))
|
||
|
||
if not isadm:
|
||
raise EnvironmentError(u'Il faut être administrateur pour effectuer cette opération.')
|
||
|
||
if type(droits)!=list:
|
||
raise TypeError(u'Une liste est attendue')
|
||
|
||
new = []
|
||
for droit in droits:
|
||
if droit == '': continue
|
||
droit = unicode(droit.strip(),'iso-8859-15')
|
||
if droit not in droits_possibles:
|
||
raise ValueError(u'Droit %s incorrect' % droit)
|
||
new.append(droit.encode('utf-8'))
|
||
|
||
if new != self._data.get('droits',[]):
|
||
self._set('droits',new)
|
||
|
||
return new
|
||
|
||
class club(base_proprietaire):
|
||
""" Classe de définition d'un club """
|
||
idn = 'cid'
|
||
objectClass = 'club'
|
||
|
||
def Nom(self,new=None):
|
||
""" Défini ou retourne le nom du club """
|
||
if new==None:
|
||
return decode(self._data.get('nom',[''])[0])
|
||
|
||
l, new = preattr(new)
|
||
new = new.capitalize()
|
||
if l<2:
|
||
raise ValueError(u"Nom trop court.")
|
||
|
||
self._set('nom',[new])
|
||
return new
|
||
|
||
def etudes(*args):
|
||
""" Etudes bidon pour club """
|
||
return u'N/A'
|
||
|
||
def nom(self):
|
||
""" Retourne le nom du club, utilisé lors de la destruction """
|
||
return strip_accents(self.Nom())
|
||
|
||
def carteEtudiant(self,pd=None):
|
||
return [ ann_scol ]
|
||
|
||
def responsable(self,adher=None):
|
||
""" Responsable du club, adher doit être une instance de la classe adhérent """
|
||
if adher==None:
|
||
aid = decode(self._data.get('responsable',[''])[0])
|
||
if aid:
|
||
return self.search('aid=%s' % aid)['adherent'][0]
|
||
else: return ''
|
||
|
||
if adher.__class__ != adherent:
|
||
raise ValueError
|
||
|
||
if not adher.id():
|
||
raise AttributeError(u'Adhérent invalide')
|
||
|
||
self._set('responsable',[adher.id()])
|
||
return adher
|
||
|
||
def chbre(self,new=None):
|
||
""" Défini le local du club
|
||
new doit être une des clefs de l'annuaire locaux_clubs"""
|
||
if new==None:
|
||
return decode(self._data.get('chbre',[''])[0])
|
||
|
||
annu = annuaires.locaux_clubs()
|
||
if new not in annu.keys():
|
||
raise ValueError(u'Local invalide',annu)
|
||
|
||
self._set('chbre',[new])
|
||
return new
|
||
|
||
def local(self):
|
||
""" Retourne le local à partir de la chambre enregistrée et
|
||
de la conversion avec l'annuaire locaux_clubs """
|
||
annu = annuaires.locaux_clubs()
|
||
return decode(annu.get(self.chbre(),''))
|
||
|
||
def compte(self,login=None):
|
||
""" Créé un compte au club sur vert"""
|
||
if login==None:
|
||
return self._data.get('uid',[''])[0]
|
||
|
||
# Génération du login : club-<login fourni>
|
||
login = login.lower()
|
||
if not sre.match('^club-', login):
|
||
login = 'club-' + login
|
||
for l in login:
|
||
if l not in string.letters + '-':
|
||
raise ValueError('Caractère %s interdit dans le login.' % l)
|
||
login = preattr(login)[1]
|
||
|
||
if 'posixAccount' in self._data['objectClass']:
|
||
if login != self._data['uid']:
|
||
# A déja un compte
|
||
raise ValueError(u"Le club à déjà un compte. Login : %s" % self._data['uid'][0])
|
||
else:
|
||
return login
|
||
|
||
if mailexist(login) and not os.system('/usr/sbin/list_lists | grep -qi %s' % login):
|
||
# la 2ème vérif est pour vérifier que ce n'est pas la ML du club
|
||
raise ValueError(u"Login existant ou correspondant à un alias mail.",1)
|
||
|
||
home = '/home/' + login.replace('-','/',1)
|
||
if os.path.exists(home):
|
||
raise ValueError(u'Création du compte impossible : home existant',1)
|
||
|
||
# Lock du mail
|
||
self.lock('mail',login)
|
||
|
||
if not 'compte' in self.modifs:
|
||
self.modifs.append('compte')
|
||
|
||
self._data['objectClass'] = [ 'club', 'posixAccount', 'shadowAccount' ]
|
||
self._data['uid'] = [ login ]
|
||
self._data['cn'] = [ preattr(self.Nom())[1] ]
|
||
self._data['loginShell' ] = [ config.club_login_shell ]
|
||
|
||
# Détermination de l'uid
|
||
uidNumber = 1000
|
||
while self.exist('(uidNumber=%s)' % uidNumber):
|
||
uidNumber += 1
|
||
try:
|
||
self.lock('uidNumber',str(uidNumber))
|
||
except:
|
||
# Quelqu'un nous a piqué l'uid que l'on venait de choisir !
|
||
return self.compte(login)
|
||
|
||
self._data['uidNumber']= [ str(uidNumber) ]
|
||
self._data['gidNumber']=[ str(config.club_gid) ]
|
||
self._data['homeDirectory']=[ preattr(home)[1] ]
|
||
|
||
return decode(login)
|
||
|
||
class machine(base_classes_crans):
|
||
""" Classe de définition d'une machine """
|
||
idn = 'mid'
|
||
|
||
def __init__(self,parent_or_tuple,typ='fixe',conn=None):
|
||
"""
|
||
parent_or_tuple est :
|
||
*soit une instance d'une classe pouvant posséder une machine
|
||
(adherent, club ou crans), la nouvelle machine lui sera alors associé.
|
||
*soit directement le tuple définissant une machine (tel que
|
||
retourné par les fonctions de recherche ldap)
|
||
typ permet de définir le type de machine : wifi ou fixe,
|
||
pris en compte uniquement pour une nouvelle machine (parent donné)
|
||
|
||
Pour édition d'une machine, typ devra être égal à 'w'
|
||
Attention, si typ ='w' mais si l'objet est déja locké il n'y a pas d'erreur
|
||
vérifier l'obtetion du lock grace à la valeur de _modifiable (si =w c'est bon)
|
||
|
||
conn est une instance de la classe de connexion à la base LDAP
|
||
"""
|
||
self.conn = conn
|
||
if not self.conn: self.connect()
|
||
self.modifs=[]
|
||
t = parent_or_tuple.__class__
|
||
if t == tuple:
|
||
# Initialisation avec données fournies
|
||
self.dn = parent_or_tuple[0]
|
||
if typ == 'w':
|
||
try:
|
||
self.lock(self.idn, self.id())
|
||
self._modifiable = 'w'
|
||
except EnvironmentError , c:
|
||
self._modifiable = 0
|
||
else:
|
||
self._modifiable = 0
|
||
self._init_data = parent_or_tuple[1].copy() # Utile pour construire l'instruction LDAP de modif
|
||
self._data = parent_or_tuple[1]
|
||
|
||
# Type de machine
|
||
if self._init_data.has_key('ipsec'):
|
||
self.__typ = 'wifi'
|
||
elif self._init_data.has_key('puissance'):
|
||
self.__typ = 'borne'
|
||
else:
|
||
self.__typ = 'fixe'
|
||
|
||
# Propriéraire inconnu mais ce n'est pas grave
|
||
self.__proprietaire = None
|
||
|
||
elif t in [ adherent, club, crans, invite ] and typ in [ 'fixe' , 'wifi' , 'borne' ]:
|
||
# Machine vide
|
||
self.__proprietaire = parent_or_tuple
|
||
self.dn = parent_or_tuple.dn
|
||
self._data={ 'objectClass': [ 'machine' ] }
|
||
if typ == 'borne':
|
||
# Valeurs par défaut
|
||
self._data['canal'] = [ '2047' ]
|
||
self._data['puissance'] = [ '60' ]
|
||
self._init_data={}
|
||
self.__typ = typ
|
||
self._modifiable = 'w'
|
||
|
||
chbre = self.__proprietaire.chbre()
|
||
# if chbre == 'EXT' and typ == 'fixe':
|
||
# raise ValueError(u'Il faut une chambre pour pouvoir posséder une machine fixe')
|
||
|
||
if chbre == '????':
|
||
raise ValueError(u'ERREUR: la chambre du propriétaire est inconnue')
|
||
|
||
if typ == 'wifi':
|
||
# Génération de la clef IPsec
|
||
self.ipsec(1)
|
||
|
||
else:
|
||
raise TypeError(u'Arguments invalides')
|
||
|
||
def Nom(self):
|
||
""" Retourne le nom de la machine """
|
||
return self.nom()
|
||
|
||
def mac(self,mac=None,multi_ok=0):
|
||
"""
|
||
Défini ou retourne l'adresse mac de la machine
|
||
Adresse valide si:
|
||
12 caractères hexa avec - ou : comme séparateurs
|
||
non nulle
|
||
Stoque l'adresse sous la forme xx:xx:xx:xx:xx:xx
|
||
Si multi_ok = 1 permet d'avoir plusieur fois la même mac sur le réseau
|
||
"""
|
||
|
||
if mac==None:
|
||
return decode(self._data.get('macAddress',[''])[0])
|
||
|
||
mac = format_mac(mac)
|
||
|
||
# La mac serait-elle déjà connue ?
|
||
if not multi_ok and self.exist('macAddress=%s' % mac):
|
||
raise ValueError(u'Mac déja utilisée sur le réseau.',1)
|
||
|
||
# La MAC serait-elle une MAC à la con ?
|
||
if mac == "00:04:4b:80:80:03":
|
||
raise ValueError(u"Il s'agit de l'unique adresse MAC achetée par nVidia pour ses cartes réseau. Il faut changer cette adresse.",2)
|
||
elif mac[0:11] == "44:45:53:54":
|
||
raise ValueError(u"Il s'agit de l'adresse MAC d'une interface PPP.", 2)
|
||
|
||
# Le test final : vendeur connu
|
||
prefix=mac[:8].upper() + ' '
|
||
vendor = ''
|
||
try:
|
||
for line in open('/usr/scripts/gestion/ethercodes.dat').readlines():
|
||
if line.startswith(prefix):
|
||
vendor = line.replace(prefix,'').strip()
|
||
break
|
||
except IOError:
|
||
# Le fichier existe pas, on sors
|
||
raise RuntimeError(u"Fichier de fabiquants de MAC non trouvé !")
|
||
|
||
if not vendor:
|
||
raise ValueError(u"Le constructeur correspondant à cette adresse MAC ne peut être trouvé.\nL'adresse MAC correspond peut-être à un pont réseau, désactivez ce pont réseau.\nContactez nounou si la MAC est bien celle d'une carte.",2)
|
||
|
||
# Lock de la mac
|
||
self.lock('macAddress',mac)
|
||
|
||
self._set('macAddress',[mac])
|
||
return mac
|
||
|
||
def mac2(self):
|
||
"""Retourne l'adresse MAC + 2"""
|
||
mac = self.mac().split(":")
|
||
mac[-1] = "%0.2x" % (int(mac[-1], 16) + 2)
|
||
return ":".join(mac)
|
||
|
||
def __host_alias(self,champ,new):
|
||
""" Vérification de la validité d'un nom de machine """
|
||
# Supression des accents
|
||
new = strip_accents(unicode(new,'iso-8859-15'))
|
||
|
||
l, new = preattr(new)
|
||
new = new.lower()
|
||
l = len(new.split('.')[0])
|
||
|
||
if l<2:
|
||
raise ValueError(u"%s trop court." % champ.capitalize())
|
||
|
||
if self.proprietaire().__class__ != crans:
|
||
new = new.split('.',1)[0]
|
||
for c in new[:]:
|
||
if not c in (string.letters + string.digits + '-'):
|
||
raise ValueError(u"Seuls les caractères alphabétiques minuscules et les - sont autorisés pour %s" % champ)
|
||
|
||
if l>17:
|
||
raise ValueError(u"%s trop long." % champ.capitalize())
|
||
|
||
if not new[0].isalnum():
|
||
raise ValueError(u"Le premier caractère du champ %s doit être alphanumérique" % champ)
|
||
|
||
# Ajout du domaine si necessaire
|
||
if new.find('.')==-1:
|
||
try:
|
||
new += '.' + config.domains[self.__typ]
|
||
except:
|
||
raise RuntimeError(u"%s : domaine non trouvé" % champ.capitalize() )
|
||
|
||
# Pas déja pris ?
|
||
if self.exist('(|(host=%s)(hostAlias=%s))' % (new,new)):
|
||
raise ValueError(u"%s : nom déjà pris" % champ.capitalize())
|
||
|
||
# Lock host
|
||
self.lock('host',new)
|
||
|
||
return new
|
||
|
||
def nom(self,new=None):
|
||
"""
|
||
Défini ou retourne le nom de machine.
|
||
Un nom de machine valide ne comporte que des caractères
|
||
alphabétiques minuscules et le -
|
||
"""
|
||
if new==None:
|
||
return decode(self._data.get('host',[''])[0])
|
||
|
||
new = self.__host_alias('nom de machine',new)
|
||
self._set('host',[new])
|
||
return new.split('.')[0]
|
||
|
||
def prise(self,new=None):
|
||
""" Retourne ou défini la prise associée à la machine
|
||
La définition n'est possible que si la machine est
|
||
une machine de l'assoce.
|
||
Si la prise est inconne retourne N/A
|
||
"""
|
||
if new == None:
|
||
if self.proprietaire().__class__ == crans:
|
||
return decode(self._data.get('prise',['N/A'])[0])
|
||
else:
|
||
chbre = self.proprietaire().chbre()
|
||
if chbre and chbre[0].lower() in annuaires.chbre_prises.keys():
|
||
try:
|
||
return annuaires.chbre_prises[chbre[0].lower()][chbre[1:]]
|
||
except:
|
||
return 'N/A'
|
||
|
||
# Attribution de la prise
|
||
new=preattr(new)[1]
|
||
if new == 'N/A':
|
||
self._set('prise',[])
|
||
self._data.pop('prise')
|
||
return
|
||
|
||
if not sre.match('^[a-cg-jmp][0-6][0-5][0-9]$',new.lower()):
|
||
raise ValueError('Prise incorrecte')
|
||
|
||
self._set('prise',[new.upper()])
|
||
return new
|
||
|
||
def alias(self,new=None):
|
||
"""
|
||
Création ou visualisation des alias d'une machine.
|
||
Même sytème d'argument que la méthode info.
|
||
"""
|
||
if not self._data.has_key('hostAlias'):
|
||
self._data['hostAlias']=[]
|
||
liste = list(self._data['hostAlias'])
|
||
if new==None: return map(decode,liste)
|
||
|
||
if type(new)==list:
|
||
# Modif
|
||
index = new[0]
|
||
new = new[1]
|
||
if new=='':
|
||
liste.pop(index)
|
||
self._set('hostAlias',liste)
|
||
return liste
|
||
else:
|
||
index=-1
|
||
|
||
# Test si valide
|
||
new = self.__host_alias('alias',new)
|
||
|
||
if index!=-1:
|
||
liste[index] = new
|
||
else:
|
||
liste = liste + [ new ]
|
||
|
||
self._set('hostAlias',liste)
|
||
return liste
|
||
|
||
def ip(self,ip=None):
|
||
"""
|
||
Défini ou retourne l'IP de la machine.
|
||
Les IP sont stoquées sous forme xxx.xxx.xxx.xxx et doivent être fournies ainsi.
|
||
Si l'IP n'est pas définie retourne <automatique>.
|
||
Si ip=<automatique> attribue la permière IP libre du sous réseau.
|
||
"""
|
||
if ip==None:
|
||
if self._data.has_key('ipHostNumber'):
|
||
return decode(self._data['ipHostNumber'][0])
|
||
elif self.proprietaire().__class__ == crans and self.__typ != 'borne':
|
||
return ''
|
||
else:
|
||
return '<automatique>'
|
||
|
||
l, ip = preattr(ip)
|
||
|
||
# Dans quel réseau la machine doit-elle être placée ?
|
||
if self.__typ == 'wifi':
|
||
if self.proprietaire().__class__ == invite:
|
||
net = config.NETs['wifi-invite']
|
||
elif self.proprietaire().etudes(0) == "ENS" and self.proprietaire().etudes(1) == "Pers":
|
||
# Personnel ENS
|
||
net = config.NETs['wifi-ens']
|
||
else:
|
||
net = config.NETs['wifi']
|
||
elif self.__typ == 'borne':
|
||
net = config.NETs['bornes']
|
||
elif self.proprietaire().__class__ == crans:
|
||
net = [ '0.0.0.0/0' ]
|
||
else:
|
||
try:
|
||
net = config.NETs[self.proprietaire().chbre()[0].lower()]
|
||
except:
|
||
raise RuntimeError(u'Impossible de trouver le réseau où placer la machine.')
|
||
|
||
if ip=='<automatique>':
|
||
pool_ip = [] # Pool d'IP à tester
|
||
for ne in net:
|
||
ip = ne.split('/')[0]
|
||
ip = ip.split('.')
|
||
n=[]
|
||
for i in ip:
|
||
n.append(int(i))
|
||
while 1:
|
||
if n[3] < 255:
|
||
n[3] += 1
|
||
else:
|
||
n[2] += 1
|
||
n[3] = 0
|
||
if n[2]==255: break
|
||
ip = "%d.%d.%d.%d" % tuple(n)
|
||
if not iptools.AddrInNet(ip,ne):
|
||
# On est allé trop loin
|
||
break
|
||
pool_ip.append(ip)
|
||
|
||
# On va prendre choisir une IP au hasard dans le pool des IP dispo
|
||
ip = ''
|
||
random.shuffle(pool_ip)
|
||
while len(pool_ip) > 0:
|
||
ip = pool_ip.pop() # On choisit une IP
|
||
if not self.exist('ipHostNumber=%s' % ip):
|
||
# On a trouvé la première ip libre
|
||
break
|
||
|
||
if ip =='':
|
||
raise RuntimeError(u'Plus d\'IP libres dans %s.' % string.join(net,' et ') )
|
||
|
||
else:
|
||
# L'ip est elle dans le bon sous réseau ?
|
||
# (accessoirement teste si l'IP est valide et ne correspond pas
|
||
# à l'adresse de broadcast ou de réseau)
|
||
if not iptools.AddrInNet(ip,net):
|
||
raise ValueError(u'IP invalide ou en dehors du sous réseau alloué.',1)
|
||
# Reformatage
|
||
ip = iptools.DecToQuad(iptools.QuadToDec(ip))
|
||
# L'ip est-elle déja allouée ?
|
||
if self.exist('ipHostNumber=%s' % ip):
|
||
raise ValueError(u'IP déjà prise.')
|
||
|
||
# Lock ip
|
||
self.lock('ipHostNumber',ip)
|
||
|
||
self._set('ipHostNumber',[ip])
|
||
return ip
|
||
|
||
def exempt(self,new=None):
|
||
"""
|
||
Liste des réseaux vers lesquels on ne compte pas l'upload
|
||
Cette liste est transférée dans la base postgres de komaz
|
||
Pour ajouter un réseau new doit être la chaîne
|
||
représentant le réseau à ajouter
|
||
Pour modifier new doit être une liste de la forme :
|
||
[ index du nouveau réseau , nouveau réseau ]
|
||
l'index est celui obtenu dans la liste retournée par exempt()
|
||
"""
|
||
if not self._data.has_key('exempt'):
|
||
self._data['exempt']=[]
|
||
liste = list(self._data['exempt'])
|
||
if new==None: return map(decode,liste)
|
||
|
||
if type(new)==list:
|
||
# Modif
|
||
index = new[0]
|
||
l, new = preattr(new[1])
|
||
if not new:
|
||
# Supression réseau
|
||
liste.pop(index)
|
||
else:
|
||
# Modif remarque
|
||
liste[index]=new
|
||
elif type(new)==str:
|
||
# Réseau supplémentaire
|
||
l, new = preattr(new)
|
||
if not new:
|
||
# On ajoute pas de réseau vide
|
||
return liste
|
||
# Ajout à la liste
|
||
liste = liste + [ new ]
|
||
else:
|
||
raise TypeError
|
||
|
||
self._set('exempt',liste)
|
||
return liste
|
||
|
||
def proprietaire(self):
|
||
"""
|
||
retroune le propriétaire de la machine (classe adherent, club ou crans ou invite)
|
||
"""
|
||
if not self.__proprietaire:
|
||
res = self.conn.search_s(','.join(self.dn.split(',')[1:]),0)
|
||
if 'adherent' in res[0][1]['objectClass'] :
|
||
self.__proprietaire = adherent(res[0],self._modifiable,self.conn)
|
||
elif 'club' in res[0][1]['objectClass'] :
|
||
self.__proprietaire = club(res[0],self._modifiable,self.conn)
|
||
elif 'invite' in res[0][0]:
|
||
self.__proprietaire = invite()
|
||
else:
|
||
self.__proprietaire = crans()
|
||
|
||
return self.__proprietaire
|
||
|
||
def ipsec(self,clef=0):
|
||
""" Génération (clef=1) ou affichage de la clef IPsec de la machine
|
||
Si clef!=1: prend la clef fournie.
|
||
"""
|
||
if self.__typ != 'wifi': return None
|
||
|
||
if not clef:
|
||
return decode(self._data.get('ipsec',[''])[0])
|
||
|
||
if clef == 1:
|
||
# Génération
|
||
clef = ''
|
||
for i in range(22):
|
||
clef += random.choice(filter(lambda x: x != 'l' and x != 'o', string.lowercase) +
|
||
filter(lambda x: x != '1' and x != '0', string.digits))
|
||
|
||
self._set('ipsec',[clef])
|
||
|
||
return clef
|
||
|
||
def canal(self,new=None,raw=False):
|
||
""" Attribution ou visualisation du canal d'une borne wifi """
|
||
if self.__typ != 'borne': return None
|
||
if not new:
|
||
canaux = self._data.get('canal',[''])[0]
|
||
if raw:
|
||
return canaux
|
||
else:
|
||
canaux = int(canaux)
|
||
if canaux < 14:
|
||
# Compatibilité ascendante
|
||
return str(canaux)
|
||
lcanal1 = []
|
||
for i in range(1,14):
|
||
found = False
|
||
if canaux & (1 << (i - 1)):
|
||
lcanal2 = []
|
||
for c in lcanal1:
|
||
if c[1] == i - 1:
|
||
lcanal2.append((c[0], i))
|
||
found = True
|
||
else:
|
||
lcanal2.append(c)
|
||
if not found:
|
||
lcanal2.append((i,i))
|
||
lcanal1 = lcanal2
|
||
lcanal3 = []
|
||
for c in lcanal1:
|
||
if c[0] == c[1]:
|
||
lcanal3.append("%d" % c[0])
|
||
else:
|
||
lcanal3.append("%d-%d" % (c[0],c[1]))
|
||
return ",".join(lcanal3)
|
||
|
||
|
||
try:
|
||
new = int(new)
|
||
if new < 0 or new > 13: raise
|
||
except:
|
||
# Nouveau système, il doit s'agir d'une liste de canaux
|
||
try:
|
||
lcanal3 = str(new).split(",")
|
||
lcanal = []
|
||
for c in lcanal3:
|
||
c2 = c.split("-")
|
||
if len(c2) == 1:
|
||
lcanal.append(int(c2[0]))
|
||
else:
|
||
for i in range(int(c2[0]), int(c2[1]) + 1):
|
||
lcanal.append(i)
|
||
new = 0
|
||
for c in lcanal:
|
||
if c not in range(0,14):
|
||
raise
|
||
new = new + (1 << (c - 1))
|
||
except:
|
||
raise ValueError(u'Canal invalide : doit être entre 0 et 13 ou une liste de canaux')
|
||
|
||
self._set('canal',[str(new)])
|
||
return new
|
||
|
||
def puissance(self,new=None):
|
||
""" Attribution ou visualisation de la puissance d'une borne wifi """
|
||
if self.__typ != 'borne': return None
|
||
if not new:
|
||
return self._data.get('puissance',[''])[0]
|
||
|
||
try:
|
||
new = int(new)
|
||
if new < -99 or new > 99: raise
|
||
except:
|
||
raise ValueError(u'Puissance invalide : doit être entre -99 et 99')
|
||
|
||
self._set('puissance',[str(new)])
|
||
return new
|
||
|
||
def save(self):
|
||
"""
|
||
Enregistre la machine courante dans la base LDAP
|
||
Retourne une chaîne indiquant les opération effectuées.
|
||
"""
|
||
if self.proprietaire().__class__ == crans and not ( isadm or user_tests.getuser() == 'www-data' ):
|
||
raise EnvironmentError(u'Il faut être administrateur pour effectuer cette opération.')
|
||
|
||
if not self._init_data:
|
||
# Nouvelle machine => configuration prise
|
||
self.services_to_restart('switch',[self.proprietaire().chbre()])
|
||
|
||
ret =''
|
||
|
||
# Besoin de redémarrer les firewalls ?
|
||
if 'ipHostNumber' in self.modifs or 'macAddress' in self.modifs:
|
||
reconf_ip = self._init_data.get('ipHostNumber',[])
|
||
reconf_ip += self._data.get('ipHostNumber',[])
|
||
else:
|
||
reconf_ip = []
|
||
|
||
# On vire les doublons dans reconf_ip
|
||
reconf_ip = list(dict(zip(reconf_ip,[None]*len(reconf_ip))))
|
||
|
||
# Enregistrement
|
||
self._save()
|
||
|
||
# Clef IPsec
|
||
if 'ipsec' in self.modifs:
|
||
ret += coul(u'Clef IPsec de la machine : %s\n' % self.ipsec(),'cyan')
|
||
self.services_to_restart('conf_wifi_ng')
|
||
|
||
# Reconfiguration firewalls et dhcps
|
||
if reconf_ip:
|
||
self.services_to_restart('macip',reconf_ip )
|
||
if self.__typ == 'wifi' :
|
||
self.services_to_restart('nectaris-dhcp')
|
||
else:
|
||
self.services_to_restart('rouge-dhcp')
|
||
if 'ports' in self.modifs:
|
||
self.services_to_restart('komaz-ports', [ self.ip() ] )
|
||
self.services_to_restart('mail_modif',['ip=%s' % self.ip()])
|
||
|
||
# Reconfiguration DNS ?
|
||
if 'host' in self.modifs or 'ipHostNumber' in self.modifs or 'hostAlias' in self.modifs:
|
||
self.services_to_restart('dns')
|
||
|
||
# Reconfiguration bornes wifi ?
|
||
if 'canal' in self.modifs or 'puissance' in self.modifs:
|
||
self.services_to_restart('conf_wifi_ng')
|
||
|
||
# Reconfiguration clients wifi ?
|
||
if self.__typ in ('wifi','borne') and ( 'ipHostNumber' in self.modifs or 'host' in self.modifs or 'macAddress' in self.modifs ):
|
||
self.services_to_restart('conf_wifi_ng')
|
||
|
||
# Regénération blackliste nécessaire ?
|
||
bl = self.blacklist_actif()
|
||
if bl and ( 'ipHostNumber' in self.modifs or 'host' in self.modifs ):
|
||
for s in bl:
|
||
self.services_to_restart(s,[ self.ip() ] )
|
||
|
||
# Regénération de l'autostatus et mail de changmement ?
|
||
if self.proprietaire().__class__ == crans:
|
||
self.services_to_restart('autostatus')
|
||
self.services_to_restart('mail_modif',['ip=%s' % self.ip()])
|
||
|
||
# Synchronisation avec la base pgsql pour les exemptions
|
||
if 'exempt' in self.modifs or 'ipHostNumber' in self.modifs:
|
||
self.services_to_restart('exemptions')
|
||
|
||
# Remise à zéro
|
||
self.modifs=[]
|
||
|
||
# Message de sortie
|
||
ret += coul(u"Machine %s enregistrée avec succès." % self._data['host'][0],'vert')
|
||
|
||
return ret
|
||
|
||
def delete(self,comment=''):
|
||
""" Destruction de la machines """
|
||
if self.proprietaire().__class__ == crans and not isadm:
|
||
raise EnvironmentError(u'Il faut être administrateur pour effectuer cette opération.')
|
||
|
||
|
||
self.proprio = self.__proprietaire.Nom() # On met dans un coin le nom du proprio
|
||
self.__proprietaire = None # On oublie le propriétaire
|
||
self._delete(self.dn,comment)
|
||
|
||
# Services à redémarrer
|
||
if self.__typ == 'wifi' :
|
||
self.services_to_restart('conf_wifi_ng')
|
||
self.services_to_restart('nectaris-dhcp')
|
||
else:
|
||
self.services_to_restart('rouge-dhcp')
|
||
|
||
self.services_to_restart('dns')
|
||
self.services_to_restart('macip',[self.ip()] )
|
||
|
||
def portTCPin(self,ports=None):
|
||
""" Ports TCP ouverts depuis l'extérieur pour la machine """
|
||
return self.__port(ports,'portTCPin')
|
||
def portTCPout(self,ports=None):
|
||
""" Ports TCP ouverts vers l'extérieur pour la machine """
|
||
return self.__port(ports,'portTCPout')
|
||
def portUDPin(self,ports=None):
|
||
""" Ports UDP ouverts vers l'extérieur pour la machine """
|
||
return self.__port(ports,'portUDPin')
|
||
def portUDPout(self,ports=None):
|
||
""" Ports UDP ouverts vers l'extérieur pour la machine """
|
||
return self.__port(ports,'portUDPout')
|
||
|
||
def __port(self,ports,champ):
|
||
if ports == None:
|
||
return self._data.get(champ,[''])[0]
|
||
|
||
ports = preattr(ports)[1]
|
||
if ports and self._data.get(champ)!=ports:
|
||
self._data[champ] = [ ports ]
|
||
if 'ports' not in self.modifs:
|
||
self.modifs.append('ports')
|
||
elif self._data.has_key(champ):
|
||
self._data.pop(champ)
|
||
if 'ports' not in self.modifs:
|
||
self.modifs.append('ports')
|
||
|
||
class _fake_proprio(crans_ldap):
|
||
""" Définitions de base d'un propriétaire virtuel """
|
||
idn = ''
|
||
def __init__(s,conn=None):
|
||
s.conn = conn
|
||
if not s.conn: s.connect()
|
||
s.dn = s.base_dn
|
||
def id(s):
|
||
return ''
|
||
def blacklist(s):
|
||
return []
|
||
def paiement(s):
|
||
return [ ann_scol ]
|
||
def carteEtudiant(s):
|
||
return [ ann_scol ]
|
||
def blacklist_actif(s):
|
||
return []
|
||
def mail(self,new=None):
|
||
return 'roots@crans.org'
|
||
def machines(s):
|
||
res = s.conn.search_s(s.dn,1,'objectClass=machine')
|
||
m = []
|
||
for r in res:
|
||
m.append(machine(r,'',s.conn))
|
||
return m
|
||
|
||
class crans(_fake_proprio):
|
||
""" Classe définissant l'assoce (pour affichage de ses machines) """
|
||
def __init__(s,conn=None):
|
||
s.conn = conn
|
||
if not s.conn: s.connect()
|
||
s.dn = s.base_dn
|
||
def Nom(s):
|
||
return u"Crans"
|
||
def chbre(s):
|
||
return u"CRA"
|
||
|
||
class invite(_fake_proprio):
|
||
""" Propriétaire des machines invité """
|
||
def __init__(s,conn=None):
|
||
s.conn = conn
|
||
if not s.conn: s.connect()
|
||
s.dn = "ou=invites," + s.base_dn
|
||
def Nom(s):
|
||
return u"Invité"
|
||
def chbre(s):
|
||
return u"N/A"
|
||
|
||
if __name__ == '__main__':
|
||
import sys
|
||
if 'lock' in sys.argv:
|
||
db = crans_ldap()
|
||
for lock in db.list_locks():
|
||
print "%s\t %s" % (lock[1]["lockid"][0],lock[0].split(',')[0])
|
||
|
||
if 'purgelock' in sys.argv:
|
||
db = crans_ldap()
|
||
db.remove_lock('*')
|
||
|
||
if 'menage' in sys.argv:
|
||
print "Ménage des machines des adhérents partis..."
|
||
db = crans_ldap()
|
||
machines=db.search('paiement!=%s&host=*.crans.org' % ann_scol ,'w')['machine']
|
||
print "Destruction de %i machines" % len(machines)
|
||
for m in machines:
|
||
print 'Destruction de %s' % m.nom()
|
||
m.delete('Ménage')
|