
on le fait après s'être connecté à la base ldap, du coup, ce dn ne sert que dans la logique des droits de lc_ldap
555 lines
24 KiB
Python
555 lines
24 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# LC_LDAP.PY-- LightWeight CransLdap
|
|
#
|
|
# Copyright (C) 2010-2013 Cr@ns <roots@crans.org>
|
|
# Authors: Antoine Durand-Gasselin <adg@crans.org>
|
|
# Nicolas Dandrimont <olasd@crans.org>
|
|
# Olivier Iffrig <iffrig@crans.org>
|
|
# Valentin Samir <samir@crans.org>
|
|
# Daniel Stan <dstan@crans.org>
|
|
# Vincent Le Gallic <legallic@crans.org>
|
|
# Pierre-Elliott Bécue <becue@crans.org>
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are met:
|
|
# * Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
# * Neither the name of the Cr@ns nor the names of its contributors may
|
|
# be used to endorse or promote products derived from this software
|
|
# without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT
|
|
# HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
## import de la lib standard
|
|
import os
|
|
import sys
|
|
import re
|
|
from contextlib import contextmanager
|
|
|
|
import ldap
|
|
|
|
## import locaux
|
|
import crans_utils
|
|
import attributs
|
|
import cimetiere
|
|
import objets
|
|
import ldap_locks
|
|
import variables
|
|
import copy
|
|
import itertools
|
|
|
|
## import de /usr/scripts/
|
|
if not "/usr/scripts/" in sys.path:
|
|
sys.path.append('/usr/scripts/')
|
|
|
|
import gestion.config as config
|
|
import cranslib.deprecated
|
|
|
|
# A priori, ldif_to_uldif et ldif_to_cldif sont obsolètes,
|
|
# du fait de l'apparition de AttrsDict dans attributs.py
|
|
def ldif_to_uldif(ldif):
|
|
"""
|
|
Transforme un dictionnaire ldif en un dictionnaire
|
|
ldif unicode.
|
|
"""
|
|
uldif = {}
|
|
for attr, vals in ldif.items():
|
|
if attr.endswith(';binary'):
|
|
binary = True
|
|
attr=attr[:-7]
|
|
else:
|
|
binary = False
|
|
attr_class = attributs.AttributeFactory.get(attr, fallback=attributs.Attr)
|
|
binary = binary or attr_class.binary
|
|
uldif[attr] = [ unicode(val, 'utf-8') if not binary else val for val in vals ]
|
|
return uldif
|
|
|
|
class lc_ldap(ldap.ldapobject.LDAPObject, object):
|
|
"""Connexion à la base ldap crans, chaque instance représente une connexion
|
|
"""
|
|
def __init__(self, dn=None, user=None, cred=None, uri=variables.uri,
|
|
readonly_dn=None, readonly_password=None):
|
|
"""Initialise la connexion ldap,
|
|
- En authentifiant avec ``dn`` et ``cred`` s'ils sont précisés
|
|
- Si ``dn`` n'est pas précisé, mais que ``user`` est précisé, récupère
|
|
le ``dn`` associé à l'uid ``user``, et effectue l'authentification
|
|
avec ce ``dn`` et ``cred``
|
|
- Sinon effectue une authentification anonyme
|
|
|
|
Si on ne se binde pas en anonyme, il faut de toutes façons fournir un ``user``.
|
|
Il sert à savoir qui fait les modifications (utilisé dans les champs historique).
|
|
|
|
"""
|
|
ldap.ldapobject.LDAPObject.__init__(self, uri)
|
|
|
|
self.lockholder = ldap_locks.LdapLockHolder(self)
|
|
|
|
if user and not re.match('[a-z_][a-z0-9_-]*', user):
|
|
raise ValueError('Invalid user name: %r' % user)
|
|
|
|
# Si un username, on récupère le dn associé…
|
|
if user and not dn:
|
|
dn = self.user_to_dn(user, readonly_dn, readonly_password)
|
|
|
|
# Si on a un dn, on se connecte avec à la base ldap sinon on s'y
|
|
# connecte en anonyme
|
|
if dn:
|
|
self.conn = self.bind_s(dn, cred)
|
|
self.dn = dn
|
|
self.droits = self.search_s(dn, ldap.SCOPE_BASE, attrlist=['droits'])[0][1].get('droits', [])
|
|
if dn == variables.admin_dn:
|
|
self.droits += [attributs.nounou]
|
|
# Il faut peupler current_login, qui sera utilisé pour écrire dans l'historique qui fait des modifications
|
|
if dn in [variables.admin_dn, variables.readonly_dn]:
|
|
# À ce stade, l'utilsateur qui appelle le script a réussi à se binder en cn=admin ou cn=readonly,
|
|
# c'est donc qu'il a pu lire les secrets, (directement ou par un sudo idoine)
|
|
# on lui fait donc confiance sur l'username qu'il fournit à condition qu'il en ait fournit un, quand même
|
|
if user is None:
|
|
raise ValueError("Même root doit préciser qui il est pour se connecter à la base LDAP.")
|
|
self.current_login = user
|
|
real_user = self.search(u'uid=%s' % user)
|
|
# Si l'utilisteur existe vraiement, on utilise les droits de cet utilisateur
|
|
if real_user:
|
|
self.droits = [str(droit) for droit in real_user[0]['droits']]
|
|
self.dn = real_user[0].dn
|
|
else:
|
|
current_user = self.search(u'uid=%s' % user)
|
|
if len(current_user) != 1:
|
|
raise ValueError("L'utilisateur %s n'est pas présent dans la base en *1* exemplaire." % user)
|
|
else:
|
|
self.current_login = unicode(current_user[0]["uid"][0])
|
|
else:
|
|
self.conn = self.simple_bind_s()
|
|
self.dn = None
|
|
self.droits = []
|
|
self._username_given = user
|
|
|
|
def __repr__(self):
|
|
if self.dn:
|
|
return str(self.__class__) + " : " + self.dn
|
|
else:
|
|
return super(lc_ldap, self).__repr__()
|
|
|
|
def gravedig(self, type, filter=None, date=None):
|
|
"""Cherche dans le cimetière un objet de type ``type``,
|
|
correspondant au filtre ``filter`` entre les dates ``date[0]`` et ``date[1]``
|
|
où la date est de la forme YYYY-MM-JJ ou - pour l'infini"""
|
|
valid=cimetiere.find(type, filter, date)
|
|
return [self.ressuscite(item) for item in valid]
|
|
|
|
@staticmethod
|
|
def ressuscite_build_ldif(ldif_file):
|
|
ldif={}
|
|
for line in open(ldif_file).readlines():
|
|
line = line.split(':',1)
|
|
if len(line)==2:
|
|
(key, value) = line
|
|
ldif[key]=ldif.get(key, []) + [value.strip()]
|
|
try:
|
|
dn = ldif['dn'][0]
|
|
del(ldif['dn'])
|
|
return (dn,ldif)
|
|
except KeyError as error:
|
|
raise KeyError("%s in %s" % (error, ldif_file))
|
|
|
|
def ressuscite(self, ldif_file, login=None):
|
|
if login is None:
|
|
login = self.current_login
|
|
(dn, ldif)= self.ressuscite_build_ldif(ldif_file)
|
|
# On définit de nouveaux dn si ceux-ci sont déjà pris
|
|
lockId = self.lockholder.newid()
|
|
try:
|
|
if self.search(dn=dn):
|
|
for id in ["aid", "mid", "fid", "cid", "xid"]:
|
|
if dn.startswith("%s=" % id):
|
|
ldif[id]=[str(self._find_id(id, lockId=lockId))]
|
|
dn="%s=%s,%s" % (id, ldif[id][0], dn.split(',',1)[1])
|
|
except ldap.NO_SUCH_OBJECT:
|
|
pass
|
|
obj = self._create_entity(dn, ldif_to_uldif(ldif), lockId)
|
|
obj.history_add(login, u"resurrection")
|
|
return obj
|
|
|
|
def user_to_dn(self, user, readonly_dn, readonly_password):
|
|
"""Cherche le dn à partir de l'username.
|
|
Cette méthode est utilisée par l'intranet2 (en mode sans CAS) car il donne l'username."""
|
|
# On commence par se binder en readonly
|
|
self.simple_bind_s(who=readonly_dn, cred=readonly_password)
|
|
res = self.search_s(variables.base_dn, 1, 'uid=%s' % user)
|
|
if len(res) < 1:
|
|
raise ldap.INVALID_CREDENTIALS({'desc': 'No such user: %s' % user })
|
|
elif len(res) > 1:
|
|
raise ldap.INVALID_CREDENTIALS({'desc': 'Too many matches: uid=%s' % user })
|
|
else:
|
|
dn = res[0][0]
|
|
return dn
|
|
|
|
def search(self, filterstr=u'(objectClass=*)', mode='ro', dn=variables.base_dn, scope=ldap.SCOPE_SUBTREE, sizelimit=1000):
|
|
"""La fonction de recherche dans la base LDAP qui renvoie un liste de
|
|
:py:class:`CransLdapObject`. On utilise la feature de ``sizelimit`` de
|
|
``python-ldap``"""
|
|
if not isinstance(filterstr, unicode):
|
|
cranslib.deprecated.usage("search ne devrait utiliser que des unicode comme filtre(%r)" % filterstr, level=2)
|
|
filterstr = filterstr.decode('utf-8')
|
|
ldap_res = self.search_ext_s(dn, scope, filterstr.encode('utf-8'), sizelimit=sizelimit)
|
|
ret = []
|
|
for dn, ldif in ldap_res:
|
|
uldif = ldif_to_uldif(ldif)
|
|
ret.append(objets.new_cransldapobject(self, dn, mode, uldif))
|
|
return ret
|
|
|
|
def machinesMulticast(self):
|
|
import cPickle
|
|
import tv.dns
|
|
import config.dns
|
|
machines = []
|
|
sap=cPickle.load(open('/usr/scripts/var/tv/sap.pickel'))
|
|
for name_ip in sap.values():
|
|
for (nom, ip) in name_ip.items():
|
|
nom=unicode(nom, 'utf-8')
|
|
nom_ascii=tv.dns.ascii(nom)
|
|
nom_punycode=tv.dns.punycode(nom)
|
|
ldif = {
|
|
'ipHostNumber' : [unicode(ip)],
|
|
'objectClass': [u'machineFixe'],
|
|
'host': [u"%s.%s" % (nom_ascii, config.dns.zone_tv)]
|
|
}
|
|
if nom_punycode:
|
|
ldif['hostAlias']=[u"%s.%s" % (nom_punycode, config.dns.zone_tv)]
|
|
machines.append(objets.machineMulticast(self, "", uldif=ldif))
|
|
return machines
|
|
|
|
def allMachinesAdherents(self, mode='ro'):
|
|
"""Renvoie la liste de toutes les machines et de tous les adherents
|
|
(club et Association Crans compris). Conçue pour s'éxécuter le plus
|
|
rapidement possible. On dumpe malgré tout toute la base."""
|
|
res = {}
|
|
parent = {}
|
|
machines = {}
|
|
# (proxying de la base ldap)
|
|
for dn, attrs in self.search_s(variables.base_dn, scope=2):
|
|
# On crée les listes des machines et propriétaires
|
|
if dn.startswith('mid='): # les machines
|
|
m = objets.new_cransldapobject(self, dn, mode, uldif=ldif_to_uldif(attrs))
|
|
parent_dn = dn.split(',', 1)[1]
|
|
if not machines.has_key(parent_dn):
|
|
machines[parent_dn] = []
|
|
machines[parent_dn].append(m)
|
|
elif (dn.startswith('aid=') or dn.startswith('cid=') or dn == variables.base_dn) and not parent.has_key(dn):
|
|
parent[dn] = objets.new_cransldapobject(self, dn, mode, uldif=ldif_to_uldif(attrs))
|
|
allmachines = []
|
|
for dn, mlist in machines.iteritems(): # on associe propriétaires et machines
|
|
parent[dn]._machines = mlist
|
|
for m in mlist:
|
|
m._proprio = parent[dn]
|
|
allmachines.append(m)
|
|
return allmachines, parent.values() # on renvoie la liste des machines et des adherents (dont club et crans)
|
|
|
|
def allMachines(self, mode='ro'):
|
|
"""Renvoie la liste de toutes les machines, Conçue pour
|
|
s'éxécuter le plus rapidement possible. On dumpe malgré tout
|
|
toute la base, c'est pour pouvoir aussi rajouter à moindre coût
|
|
les propriétaires."""
|
|
machines, _ = self.allMachinesAdherents(mode)
|
|
return machines
|
|
|
|
def allAdherents(self, mode='ro'):
|
|
"""Renvoie la liste de toutes les adherents, Conçue pour
|
|
s'éxécuter le plus rapidement possible. On dumpe malgré tout
|
|
toute la base, c'est pour pouvoir aussi rajouter à moindre coût
|
|
les machines."""
|
|
_, adherents = self.allMachinesAdherents(mode)
|
|
return adherents
|
|
|
|
@contextmanager
|
|
def newMachine(self, parent, realm, mldif, login=None):
|
|
"""
|
|
Crée une nouvelle machine: ``realm`` peut être:
|
|
fil, adherents-v6, wifi, wifi-adh-v6, adm, gratuit, personnel-ens, special
|
|
mldif est un uldif pour la machine
|
|
--Partiellement implémenté
|
|
|
|
Doit être utilisé avec un context manager, c'est à dire comme ci-dessous :
|
|
1: with newMachine(parent, realm, mldif) as machine:
|
|
2: machine.create()
|
|
3: print machine
|
|
La fonction est executé jusqu'au yield à la ligne 1, puis son exécution reprend
|
|
au niveau du yield jusqu'à la fin de la fonction à la ligne 3, en sortant du contexte
|
|
Une fois sorti du contexte, il n'est plus possible d'effectuer des actions d'écriture
|
|
sur l'objet.
|
|
"""
|
|
lockId = self.lockholder.newid()
|
|
# On ne veut pas modifier mldif directement
|
|
uldif = copy.deepcopy(mldif)
|
|
if login is None:
|
|
login = self.current_login
|
|
#adm, serveurs, bornes, wifi, adherents, gratuit ou personnel-ens"""
|
|
owner = self.search(u'objectClass=*', dn=parent, scope=0)[0]
|
|
|
|
if realm in ["adm", "serveurs", "serveurs-v6", "adm-v6"]:
|
|
uldif['objectClass'] = [u'machineCrans']
|
|
assert isinstance(owner, objets.AssociationCrans)
|
|
|
|
elif realm == "bornes":
|
|
uldif['objectClass'] = [u'borneWifi']
|
|
assert isinstance(owner, objets.AssociationCrans)
|
|
|
|
elif realm in ["wifi-adh", "wifi-adh-v6"]:
|
|
uldif['objectClass'] = [u'machineWifi']
|
|
assert isinstance(owner, objets.adherent) or isinstance(owner, objets.club)
|
|
|
|
elif realm in ["adherents", "adherents-v6", "personnel-ens"]:
|
|
uldif['objectClass'] = [u'machineFixe']
|
|
assert isinstance(owner, objets.adherent) or isinstance(owner, objets.club)
|
|
|
|
else:
|
|
raise ValueError("Realm inconnu: %r" % realm)
|
|
|
|
try:
|
|
# On récupère le premier id libre dans la plages s'il n'est pas
|
|
# déjà précisé dans le ldif
|
|
rid = uldif.setdefault('rid', [unicode(self._find_id('rid', realm, lockId=lockId))])
|
|
|
|
# La machine peut-elle avoir une ipv4 ?
|
|
if 'v6' not in realm:
|
|
uldif['ipHostNumber'] = [ unicode(crans_utils.ip4_of_rid(int(rid[0]))) ]
|
|
|
|
ip6 = unicode(crans_utils.ip6_of_mac(uldif['macAddress'][0], int(rid[0])))
|
|
uldif['ip6HostNumber'] = [ ip6 ] if ip6 else []
|
|
|
|
# Mid
|
|
uldif['mid'] = [ unicode(self._find_id('mid', lockId=lockId)) ]
|
|
|
|
# Tout doit disparaître !!
|
|
machine = self._create_entity('mid=%s,%s' % (uldif['mid'][0], parent), uldif, lockId)
|
|
machine.__enter__()
|
|
if machine.may_be(variables.created, self.droits + self._check_parent(machine.dn)):
|
|
yield machine
|
|
else:
|
|
raise EnvironmentError("Vous n'avez pas le droit de créer cette machine.")
|
|
finally:
|
|
try:
|
|
machine.__exit__(None, None, None)
|
|
except NameError:
|
|
self.lockholder.purge(lockId)
|
|
|
|
@contextmanager
|
|
def newAdherent(self, aldif):
|
|
"""Crée un nouvel adhérent
|
|
Doit être utilisé avec un context manager, voir newMachine pour plus de détails"""
|
|
lockId = self.lockholder.newid()
|
|
uldif = copy.deepcopy(aldif)
|
|
try:
|
|
aid = uldif.setdefault('aid', [ unicode(self._find_id('aid', lockId=lockId)) ])
|
|
uldif['objectClass'] = [u'adherent']
|
|
adherent = self._create_entity('aid=%s,%s' % (aid[0], variables.base_dn), uldif, lockId)
|
|
adherent.__enter__()
|
|
if adherent.may_be(variables.created, self.droits):
|
|
yield adherent
|
|
else:
|
|
raise EnvironmentError("Vous n'avez pas le droit de créer cet adhérent.")
|
|
finally:
|
|
try:
|
|
adherent.__exit__(None, None, None)
|
|
except NameError:
|
|
self.lockholder.purge(lockId)
|
|
|
|
@contextmanager
|
|
def newClub(self, cldif):
|
|
"""Crée un nouveau club
|
|
Doit être utilisé avec un context manager, voir newMachine pour plus de détails"""
|
|
lockId = self.lockholder.newid()
|
|
uldif = copy.deepcopy(cldif)
|
|
try:
|
|
cid = uldif.setdefault('cid', [ unicode(self._find_id('cid', lockId=lockId)) ])
|
|
uldif['objectClass'] = [u'club']
|
|
club = self._create_entity('cid=%s,%s' % (cid[0], variables.base_dn), uldif, lockId)
|
|
club.__enter__()
|
|
if club.may_be(variables.created, self.droits):
|
|
yield club
|
|
else:
|
|
raise EnvironmentError("Vous n'avez pas le droit de créer cet adhérent.")
|
|
finally:
|
|
try:
|
|
club.__exit__(None, None, None)
|
|
except NameError:
|
|
self.lockholder.purge(lockId)
|
|
|
|
@contextmanager
|
|
def newFacture(self, parent, fldif):
|
|
"""Crée une nouvelle facture
|
|
Doit être utilisé avec un context manager, voir newMachine pour plus de détails"""
|
|
lockId = self.lockholder.newid()
|
|
uldif = copy.deepcopy(fldif)
|
|
try:
|
|
# fid
|
|
uldif['fid'] = [ unicode(self._find_id('fid', lockId=lockId)) ]
|
|
uldif['objectClass'] = [u'facture']
|
|
facture = self._create_entity('fid=%s,%s' % (uldif['fid'][0], parent), uldif, lockId)
|
|
facture.__enter__()
|
|
if facture.may_be(variables.created, self.droits + self._check_parent(facture.dn)):
|
|
yield facture
|
|
else:
|
|
raise EnvironmentError("Vous n'avez pas le droit de créer cette facture.")
|
|
finally:
|
|
try:
|
|
facture.__exit__(None, None, None)
|
|
except NameError:
|
|
self.lockholder.purge(lockId)
|
|
|
|
|
|
@contextmanager
|
|
def newCertificat(self, parent, xldif):
|
|
"""Crée un nouveau certificat x509
|
|
Doit être utilisé avec un context manager, voir newMachine pour plus de détails"""
|
|
lockId = self.lockholder.newid()
|
|
uldif = copy.deepcopy(xldif)
|
|
try:
|
|
# xid
|
|
uldif['xid'] = [ unicode(self._find_id('xid', lockId=lockId)) ]
|
|
uldif['objectClass'] = [u'baseCert']
|
|
baseCert = self._create_entity('xid=%s,%s' % (uldif['xid'][0], parent), uldif, lockId)
|
|
baseCert.__enter__()
|
|
if baseCert.may_be(variables.created, self.droits + self._check_parent(baseCert.dn)):
|
|
yield baseCert
|
|
else:
|
|
raise EnvironmentError("Vous n'avez pas le droit de créer ce certiticat.")
|
|
finally:
|
|
try:
|
|
baseCert.__exit__(None, None, None)
|
|
except NameError:
|
|
self.lockholder.purge(lockId)
|
|
|
|
def _create_entity(self, dn, uldif, lockId):
|
|
'''Crée une nouvelle entité ldap avec le dn ``dn`` et les
|
|
attributs de ``ldif``. Attention, ldif doit contenir des
|
|
données encodées.'''
|
|
# Ajout des locks, on instancie les attributs qui ne sont pas
|
|
# des id, ceux-ci étant déjà lockés.
|
|
try:
|
|
for key, values in uldif.iteritems():
|
|
attribs = [attributs.attrify(val, key, self) for val in values]
|
|
for attribut in attribs:
|
|
if attribut.unique:
|
|
try:
|
|
self.lockholder.addlock(key, str(attribut), Id=lockId)
|
|
except ldap_locks.LdapLockedByMySelf:
|
|
# On vient juste d'acquérir le lock dans _find_id, ça n'est pas grâve
|
|
pass
|
|
return objets.new_cransldapobject(self, dn, 'rw', uldif, lockId=lockId)
|
|
except ldap_locks.LockError:
|
|
# On supprime seulement les locks que l'on vient de poser
|
|
self.lockholder.purge(lockId)
|
|
raise
|
|
|
|
def _find_id(self, attr, realm=None, lockId=None):
|
|
'''Trouve un id libre. Si une plage est fournie, cherche
|
|
l'id dans celle-ci, sinon, prend le plus élevé possible.'''
|
|
if lockId is None:
|
|
raise ValueError("lockId ne devrait pas être à None")
|
|
|
|
plage = None
|
|
# On récupère la plage des ids
|
|
if realm != None:
|
|
plage = itertools.chain(*[xrange(a,b+1) for (a,b) in config.rid_primaires[realm]])
|
|
|
|
# Si plage vaut None, on veux un id strictement croissant
|
|
if plage is None:
|
|
# On essaye de récupérer le dernier id si on l'a déjà vu passer
|
|
try:
|
|
last_id = open('/tmp/lc_ldap_lastid_%s_%s' % (attr, os.getuid())).read().strip()
|
|
except IOError:
|
|
last_id = 0
|
|
# On récupère tous les id plus grand que le dernier que l'on connait
|
|
res = self.search_s(variables.base_dn, ldap.SCOPE_SUBTREE, '%s>=%s' % (attr, last_id), attrlist = [attr])
|
|
# Si jamais id n'a pas de methode ORDERING, on récupère une liste vide et on fallback en récupérant tous les id (c'est lent)
|
|
if res == []:
|
|
res = self.search_s(variables.base_dn, ldap.SCOPE_SUBTREE, '%s=*' % attr, attrlist = [attr])
|
|
else:
|
|
# On récupère tous les id
|
|
res = self.search_s(variables.base_dn, ldap.SCOPE_SUBTREE, '%s=*' % attr, attrlist = [attr])
|
|
|
|
if plage != None:
|
|
# On extrait seulement les valeurs des id qui nous intêressent, dans un set
|
|
# car on n'a pas besoin de trier et que et que i in set c'est O(1) contre O(n) pour les list
|
|
nonfree = set(int(r[1].get(attr)[0]) for r in res if r[1].get(attr))
|
|
for i in plage:
|
|
if i in nonfree:
|
|
continue
|
|
else:
|
|
# On crée l'attribut associé, pour parser sa valeur.
|
|
my_id = attributs.attrify(unicode(i), attr, self, None)
|
|
if my_id.value != i:
|
|
continue
|
|
else:
|
|
try:
|
|
self.lockholder.addlock(attr, str(i), lockId)
|
|
break
|
|
except ldap_locks.LockError:
|
|
continue
|
|
else:
|
|
raise EnvironmentError('Aucun %s libre dans la plage [%d, %d]' %
|
|
(attr, plage[0], i))
|
|
else:
|
|
# On extrait seulement les valeurs des id qui nous intêressent dans une liste
|
|
nonfree = [ int(r[1].get(attr)[0]) for r in res if r[1].get(attr) ]
|
|
# On trie pour récupérer le dernier
|
|
nonfree.sort()
|
|
try:
|
|
last_id = nonfree[-1]
|
|
except IndexError:
|
|
last_id = 0
|
|
|
|
# On écrit le nouveau dernier id connu
|
|
f=os.open('/tmp/lc_ldap_lastid_%s_%s' % (attr, os.getuid()), os.O_WRONLY | os.O_CREAT, 0600)
|
|
os.write(f, str(last_id))
|
|
os.close(f)
|
|
|
|
i = last_id + 1
|
|
while True:
|
|
try:
|
|
self.lockholder.addlock(attr, str(i), lockId)
|
|
break
|
|
except ldap_locks.LockError:
|
|
i += 1
|
|
return i
|
|
|
|
|
|
def _check_parent(self, objdn):
|
|
"""
|
|
Teste le rapport entre le dn fourni et self
|
|
Retourne une liste qui s'ajoutera à la liste des droits
|
|
"""
|
|
|
|
if objdn.endswith(self.dn) and objdn != self.dn:
|
|
return [attributs.parent]
|
|
|
|
else:
|
|
return []
|
|
|
|
|
|
def _check_self(self, objdn):
|
|
"""
|
|
Teste si le dn fourni est celui de self.
|
|
Retourne une liste qui s'ajoutera à la liste des droits
|
|
"""
|
|
|
|
if objdn == self.dn:
|
|
return [attributs.soi]
|
|
else:
|
|
return []
|