lc_ldap/crans_utils.py
Pierre-Elliott Bécue 42c48f77e8 Quelques améliorations, mise en place de fonctions pour tester parenté,
tester si c'est soi-même qu'on modifie, pour tester si on peut bien
altérer l'objet concerné.
Création des objets en deux temps (on crée l'objet Crans, puis
on l'enregistre dans ldap après test des droits.
Changement de méthode de binding : le binding nominatif va être bien
trop complexe à implémenter, on va donc faire autrement...
2013-01-28 00:45:01 +01:00

174 lines
6.3 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# CRANS_UTILS.PY-- Utils for Cr@ns gestion
#
# Copyright (c) 2010, Cr@ns <roots@crans.org>
# Author: Antoine Durand-Gasselin <adg@crans.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Cr@ns nor the names of its contributors may
# be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import calendar, netaddr, re, time, smtplib, sys
sys.path.append('/usr/scripts/gestion')
import config
from unicodedata import normalize
def ip4_of_rid(rid):
"""Convertit un rid en son IP associée"""
for net, plage in config.rid.items():
if rid >= plage[0] and rid <= plage[1]:
break
else:
raise ValueError("Rid dans aucune plage: %d" % rid)
if net == 'special':
try:
return netaddr.IPAddress(config.rid_machines_speciales[rid])
except KeyError:
return ValueError(u"Machine speciale inconnue: %d" % rid)
return netaddr.IPAddress(netaddr.IPNetwork(config.NETs[net][0]).first + rid - plage[0])
def prefixev6_of_rid(rid):
"""
L'ip de sous-réseau privé d'une machine. L'adhérent en fait ce qu'il veut, mais c'est la machine
associée au rid qui est responsable du traffic.
Cette fonction retourne l'ip de début de ce sous-réseau.
"""
for net, plage in config.rid.items():
if rid >= plage[0] and rid <= plage[1]:
break
else:
raise ValueError("Rid dans aucune plage: %d" % rid)
# fil-v6 ou wifi-v6, we don't care
return netaddr.IPAddress(netaddr.IPNetwork(config.prefix['fil-v6'][0]).first + 2**64*rid)
def ip6_of_mac(mac, rid):
"""
Retourne la bonne ipv6 de la machine en fonction de sa mac et de son rid.
"""
for net, plage in config.rid.items():
if rid >= plage[0] and rid <= plage[1]:
break
else:
raise ValueError("Rid dans aucune plage: %d" % rid)
# En théorie, format_mac est inutile, car on ne devrait avoir
# que des mac formatées.
mac = format_mac(mac).replace(':', '')
# hex retourne un str, donc on concatène, suivant la RFC
euid64v6 = hex(int(mac[:2], 16)^0b00000010) + mac[2:6] + 'fffe' + mac[6:12]
# fil-v6 ou wifi-v6, we don't care
return netaddr.IPAddress(netaddr.IPNetwork(config.prefix[net][0]).first + int(euid64v6, 16))
def strip_accents(a):
""" Supression des accents de la chaîne fournie"""
res = normalize('NFKD', a).encode('ASCII', 'ignore')
return unicode(res)
def strip_spaces(a):
""" Suppression des espaces et des apostrophes"""
return a.replace(u' ', u'_').replace(u"'", u'')
def mailexist(mail):
"""Vérifie si une adresse mail existe ou non grace à la commande
vrfy du serveur mail """
mail = mail.split('@', 1)[0]
# try:
s = smtplib.SMTP('smtp.adm.crans.org')
s.putcmd("vrfy", mail)
r = s.getreply()[0] in [250, 252]
s.close()
# except:
# raise ValueError(u'Serveur de mail injoignable')
return r
def format_ldap_time(tm):
u"""Formatage des dates provenant de la base LDAP
Transforme la date YYYYMMDDHHMMSS.XXXXXXZ (UTC)
en date DD/MM/YY HH:MM (local)"""
tm_st = time.strptime(tm.split('.')[0], "%Y%m%d%H%M%S") # struct_time UTC
timestamp = calendar.timegm(tm_st)
tm_st = time.localtime(timestamp) # struct_time locale
return time.strftime("%d/%m/%Y %H:%M", tm_st)
def format_mac(mac):
u""" Formatage des adresses mac
Transforme une adresse pour obtenir la forme xx:xx:xx:xx:xx:xx
Retourne la mac formatée.
"""
mac = netaddr.EUI(mac)
if not mac:
raise ValueError(u"MAC nulle interdite\nIl doit être possible de modifier l'adresse de la carte.")
return str(mac).replace('-', ':')
def format_tel(tel):
u"""Formatage des numéros de téléphone
Transforme un numéro de téléphone pour ne contenir que des chiffres
(00ii... pour les numéros internationaux)
Retourne le numéro formaté.
"""
tel_f = tel.strip()
if tel_f.startswith(u"+"):
tel_f = u"00" + tel_f[1:]
if u"(0)" in tel_f:
tel_f = tel_f.replace(u"(0)", u"")
tel_f = re.sub(r'\D', '', tel_f)
return tel_f
def validate_name(value, more_chars=""):
"""Valide un nom: ie un unicode qui contient lettres, espaces et
apostrophes, et éventuellement des caractères additionnels"""
if re.match("^[A-Za-z0-9]([-' %s]?[A-Za-z0-9])*$" % more_chars,
normalize('NFKD', value).encode('ASCII', 'ignore')):
return value
else:
raise ValueError("Nom invalide ('%s')" % value)
def ldap_sanitize(s):
""" Échappe les caractères spéciaux ldap.
(Si vous avez une méthode plus propre pour coder ça, je suis preneur)
Todo: rajouter conversion pour caractère utf-8 multioctets ?
(cf http://blog.dzhuvinov.com/?p=585 )
"""
replace = {
'*': '\\2a',
'(': '\\28',
')': '\\29',
'\\': '\\5c',
'\x00': '\\00'}
def conv(c):
try: return replace[c]
except KeyError: return c
return "".join([conv(c) for c in s])