darcs-hash:20060319183602-68412-2d6a5d90ea6648879e95bc4f36a25250d5a561a0.gz
This commit is contained in:
glondu 2006-03-19 19:36:02 +01:00
parent f9af7f43ae
commit 87e88a6443

View file

@ -27,16 +27,16 @@ random.seed() # On initialise le g
##################################################################################
### paramètres de connaction à la base LDAP
if __name__=='ldap_crans_test' or os.environ.get('crans_ldap','')=='test':
if __name__ == 'ldap_crans_test' or os.environ.get('crans_ldap', '') == 'test':
# Utilisation de la base de données de test (tests, séminaire...)
# Il faut au choix :
# - faire un import crans_ldap_test
# ou - crans_ldap=test /le/script
uri = ro_uri = 'ldap://egon.adm.crans.org/'
ldap_auth_dn='cn=admin,dc=crans,dc=org'
ldap_password='75bdb64f32'
ldap_auth_dn = 'cn=admin,dc=crans,dc=org'
ldap_password = '75bdb64f32'
elif user_tests.getuser()=='freerad':
elif user_tests.getuser() == 'freerad':
# Freeradius n'a pas accès au secret, donc accès que en local
uri = ''
ro_uri = 'ldapi://%2fvar%2frun%2fldapi/'
@ -50,28 +50,30 @@ else:
try:
from secrets import ldap_password, ldap_auth_dn
except:
sys.stdout.write(coul('Warning : impossible de lire le fichier de secret !\n','jaune'))
sys.stdout.write(coul('Warning : impossible de lire le fichier de secret !\n', 'jaune'))
sys.exit(1)
# uri pour les instances de crans_ldap faisant de la lecture seule
if os.path.exists('/var/run/ldapi') :
if os.path.exists('/var/run/ldapi'):
ro_uri = 'ldapi://%2fvar%2frun%2fldapi/'
else :
else:
ro_uri = uri
##################################################################################
### Items de la blackliste
blacklist_items = { u'bloq' : u'Bloquage total de tous les services' ,
blacklist_items = { u'bloq' : u'Bloquage total de tous les services',
u'virus' : u'Bloquage sur squid',
u'upload' : u'Bloquage total de l\'accès à l\'extérieur',
u'upload' : u"Bloquage total de l'accès à l'extérieur",
u'warez' : u'Bloquage sur squid',
u'p2p' : u'Bloquage total de l\'accès à l\'extérieur',
u'p2p' : u"Bloquage total de l'accès à l'extérieur",
u'autodisc_upload' : u'Autodisconnect pour upload',
u'autodisc_p2p' : u'Autodisconnect pour P2P'}
u'autodisc_p2p' : u'Autodisconnect pour P2P' }
##################################################################################
### 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' ]
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
@ -81,7 +83,7 @@ ann_scol = config.ann_scol
script_utilisateur = user_tests.getuser()
##################################################################################
### Fonctions utiles
### Fonctions utiles
def decode(s):
"""
Retourne un unicode à partir de s
@ -143,10 +145,10 @@ def preattr(val):
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') ]
return [len(val), unicode(val,'iso-8859-1').encode('utf-8')]
elif t==unicode:
val = val.strip()
return [ len(val) , val.encode('utf-8') ]
return [len(val), val.encode('utf-8')]
else:
raise TypeError
@ -176,23 +178,23 @@ def format_mac(mac):
Retourne la mac formatée.
"""
l, mac = preattr(mac)
mac = mac.strip().replace("-",":")
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 de l\'adresse mac incorrecte.')
mac = mac.replace(':', '').lower()
if len(mac) != 12:
raise ValueError(u"Longueur de l'adresse mac incorrecte.")
for c in mac[:]:
if c not in string.hexdigits:
raise ValueError(u"Caractère interdit '%s' dans l\'adresse mac." % c)
if mac=='000000000000':
raise ValueError(u"Caractère interdit '%s' dans l'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:] )
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
@ -201,36 +203,36 @@ def format_mac(mac):
class service:
""" Définit un service à redémarrer """
def __init__(self,nom,args=[],start=[]):
def __init__(self, nom, args=[], start=[]):
"""
Nom du service
Liste des arguments
Liste d'horaires de démarrages
"""
self.nom=nom
self.args=args
self.start=map(int,start)
self.nom = nom
self.args = args
self.start = map(int, start)
def __str__(self):
starting = self.start
starting.sort()
dates = ' et '.join(map(lambda t: t<time.time() and \
dates = ' et '.join(map(lambda t: t < time.time() and \
"maintenant" or time.strftime(date_format,
time.gmtime(t)),
self.start))
dates = " à partir d%s %s" % (dates.startswith("maintenant") and "e" or "u",
dates)
return ("%s(%s)%s" % (self.nom,
','.join(self.args),
dates)).replace(" et maintenant","")
','.join(self.args),
dates)).replace(" et maintenant", "")
class crans_ldap:
"""
Classe de connexion à la base LDAP du crans.
"""
conn=None
base_dn='ou=data,dc=crans,dc=org'
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'
@ -243,7 +245,7 @@ class crans_ldap:
'mac': 'macAddress',
'ip': 'ipHostNumber',
'telephone': 'tel',
'position': 'positionBorne'}
'position': 'positionBorne' }
# Les différentes classes LDAP de machines
ldap_machines_classes = ['machineFixe', 'machineWifi', 'machineCrans', 'borneWifi']
@ -287,11 +289,14 @@ class crans_ldap:
search_champs[i] = auto_search_champs[i] + non_auto_search_champs[i]
# Profondeur des différentes recherches (scope)
scope = {'adherent': 1, 'club': 1,
'machineFixe': 2, 'machineWifi': 2,
'machineCrans': 2, 'borneWifi': 2 }
scope = { 'adherent': 1,
'club': 1,
'machineFixe': 2,
'machineWifi': 2,
'machineCrans': 2,
'borneWifi': 2 }
def __init__(self,readonly=False):
def __init__(self, readonly=False):
self.connect(readonly)
def __del__(self):
@ -300,7 +305,7 @@ class crans_ldap:
for lock in self._locks:
self.remove_lock(lock)
def connect(self,readonly=False):
def connect(self, readonly=False):
""" Initialisation la connexion vers le serveur LDAP """
if readonly :
self.conn = ldap.initialize(ro_uri)
@ -310,7 +315,7 @@ class crans_ldap:
nbessais = 0
while True :
try:
self.conn.bind_s(ldap_auth_dn,ldap_password,ldap.AUTH_SIMPLE)
self.conn.bind_s(ldap_auth_dn, ldap_password, ldap.AUTH_SIMPLE)
break
except ldap.SERVER_DOWN:
nbessais += 1
@ -320,7 +325,7 @@ class crans_ldap:
else:
sleep(0.3)
def exist(self,arg):
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.
@ -334,10 +339,10 @@ class crans_ldap:
Exemple : exist('chbre=Z345') vérifie si il y a un adhérent en Z345
"""
r=[]
r = []
# Premier test: dans les objets déjà inscrits
ret = self.conn.search_s(self.base_dn,2,arg)
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'attribut dn => erreur
@ -348,8 +353,8 @@ class crans_ldap:
r.append(res[0])
# Deuxième test : lock ?
ret = self.conn.search_s(self.base_lock,1,arg)
lockid = '%s-%s' % (hostname, os.getpid() )
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]
@ -357,7 +362,7 @@ class crans_ldap:
# 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] ):
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
@ -367,7 +372,7 @@ class crans_ldap:
return r
def lock(self,item,valeur):
def lock(self, item, valeur):
"""
Lock un item avec la valeur valeur, les items possibles
peuvent être :
@ -384,18 +389,18 @@ class crans_ldap:
# 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 } )
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)
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]
res = self.conn.search_s(lock_dn, 2, 'objectClass=lock')[0]
l = res[1]['lockid'][0]
except: l = '%s-1' % hostname
if l != lockid:
@ -404,7 +409,7 @@ class crans_ldap:
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
return self.lock(item, valeur) # relock
raise EnvironmentError(u'Objet (%s=%s) locké, patienter.' % (item, valeur), l)
else:
if not hasattr(self,'_locks'):
@ -412,13 +417,13 @@ class crans_ldap:
else:
self._locks.append(lock_dn)
def remove_lock(self,lockdn):
def remove_lock(self, lockdn):
"""
Destruction d'un lock
Destruction de tous les locks si lockdn=*
"""
# Mettre des verifs ?
if lockdn!='*':
if lockdn != '*':
self.conn.delete_s(lockdn)
try:
self._locks.remove(lockdn)
@ -434,7 +439,7 @@ class crans_ldap:
""" Liste les locks """
return self.conn.search_s(self.base_lock,1,'objectClass=lock')
def services_to_restart(self,new=None,args=[],start=0):
def services_to_restart(self, new=None, args=[], start=0):
"""
start indique la date (en secondes depuis epoch) à partir du
moment cette action sera effectué
@ -495,9 +500,9 @@ class crans_ldap:
# Petite fonction à appliquer aux arguments
if type(args) == str: args = [ args ]
args=map(lambda x:preattr(x)[1] ,args)
args=map(lambda x:preattr(x)[1], args)
if type(start) == int: start = [ start ]
start=map(lambda x:preattr(x)[1] ,start)
start=map(lambda x:preattr(x)[1], start)
if new in serv.keys():
modlist = []
@ -527,9 +532,9 @@ class crans_ldap:
# else rien à faire
else:
# Entrée non présente -> ajout
modlist = ldap.modlist.addModlist({ 'objectClass': 'service' ,
'cn': new ,
'args': args ,
modlist = ldap.modlist.addModlist({ 'objectClass': 'service',
'cn': new,
'args': args,
'start': start } )
try:
self.conn.add_s(serv_dn,modlist)
@ -883,13 +888,13 @@ class base_classes_crans(crans_ldap):
"""
Blacklistage de la ou de toutes la machines du propriétaire
new est une liste de 4 termes:
[ debut_sanction , fin_sanction, sanction, commentaire ]
[ 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 )
( 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'):
@ -972,7 +977,7 @@ class base_classes_crans(crans_ldap):
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 ]
[ 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'):
@ -1130,11 +1135,11 @@ class base_classes_crans(crans_ldap):
else:
### Modification entrée
if not self._modifiable:
raise RuntimeError(u'Objet non modifiable : %s'%str(self))
raise RuntimeError(u'Objet non modifiable : %s' % str(self))
modlist = ldap.modlist.modifyModlist(self._init_data,self._data)
try:
self.conn.modify_s(self.dn,modlist)
except ldap.TYPE_OR_VALUE_EXISTS , c:
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)
@ -1142,7 +1147,7 @@ class base_classes_crans(crans_ldap):
# 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)
self.services_to_restart("blacklist_%s"%n, [], t)
# Reinitialisation
self._init_data = self._data.copy()
@ -1833,7 +1838,7 @@ class adherent(base_proprietaire):
return True
def compte(self,login=None,uidNumber=0,hash_pass='',shell=config.login_shell):
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)
@ -1861,27 +1866,27 @@ class adherent(base_proprietaire):
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:
if l < 2:
raise ValueError(u"Login trop court.")
if l>config.maxlen_login:
if l > config.maxlen_login:
raise ValueError(u"Login trop long.")
if login[0]=='-':
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)
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)
raise ValueError(u'Création du compte impossible : home existant', 1)
if os.path.exists("/var/mail/"+login):
raise ValueError(u'Création du compte impossible : /var/mail/%s existant'%login,1)
if os.path.exists("/var/mail/" + login):
raise ValueError(u'Création du compte impossible : /var/mail/%s existant' % login, 1)
# Lock du mail
self.lock('mail',login)
self.lock('mail', login)
self._data['mail']= [ login ]
self._data['mail'] = [login]
if not 'compte' in self.modifs:
self.modifs.setdefault('compte', None)
@ -1890,15 +1895,15 @@ class adherent(base_proprietaire):
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['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 ]
self._data['loginShell'] = [shell]
if hash_pass:
self._data['userPassword'] = [ hash_pass ]
self._data['userPassword'] = [hash_pass]
if uidNumber:
if self.exist('(uidNumber=%s)' % uidNumber):
@ -1913,7 +1918,7 @@ class adherent(base_proprietaire):
pool_uid.append(uidNumber)
break
if not len(pool_uid):
raise ValueError(u'Plus d\'uid disponibles !')
raise ValueError(u"Plus d'uid disponibles !")
try:
self.lock('uidNumber',str(uidNumber))
@ -1921,8 +1926,8 @@ class adherent(base_proprietaire):
# 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['uidNumber']= [str(uidNumber)]
self._data['gidNumber'] = [str(config.gid)]
self._data['homeDirectory']=[ preattr(home)[1] ]
gecos = '%s %s' % tuple(map(lambda x: strip_accents(x.capitalize()), (self.prenom(), self.nom())))
@ -2371,7 +2376,7 @@ class Machine(base_classes_crans):
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.
Si ip=<automatique> attribue la permière IP libre du sous-réseau.
"""
if ip == None:
if self._data.has_key('ipHostNumber'):
@ -2433,14 +2438,14 @@ class Machine(base_classes_crans):
break
if not len(pool_ip):
raise RuntimeError(u'Plus d\'IP libres dans %s.' % string.join(net, ' et '))
raise RuntimeError(u"Plus d'IP libres dans %s." % string.join(net, ' et '))
else:
# L'ip est elle dans le bon sous réseau ?
# 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)
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 ?