Hogyan rendezzek szerver oldalon LDAP search kérést 2 attribútum szerint ?

+3 votes
asked Mar 30, 2016 in IRF tantárgy by Szepi (27 points)  

Egy LDAP keresés folyamán szerver oldali rendezést is próbálok végrehajtani:

from bonsai import LDAPClient
client = LDAPClient("ldap://localhost")
conn = client.connect()
searchresults = conn.search("dc=irf,dc=local", 2, "(telephoneNumber=666)", ["sn", "gn", "telephoneNumber"], sortorder=["sn", "gn"])

Ez az API dokumentációban leírtak alapján egy ldapsearchiter objektumot ad vissza, aminek egyetlen metódusa van, az acquirenextpage() metódus, ami egy message_ID-t ad vissza.

searchresults
<
bonsai.ldapsearchiter object at 0xb63ab250>

Az API dokumentáció alapján próbáltam megtalálni, hogy mit lehet kezdeni egy ilyen messageID-val és az LDAPConnection.getresult() metódusát találtam, viszont azzal a következő hibát kapom, ami nem túl beszédes.

page = conn.getresult(searchresults.acquirenextpage(), timeout=None)
Traceback (most recent call last):
File "", line 1, in
AttributeError: Wrong parameter.

Ha a következő ciklust használom, akkor a result változóban megkapom az LDAPEntry objektumokat, azonban azoknak a sorrendje nem tűnik az elvártaknak megfelelően rendezettnek (talán nem sikerül a rendezés?):

for result in search_results:
... print(result)
... print("\n")

A keresést kipróbáltam a rendezés rész nélkül is, így egy egyszerű LDAPEntry listát kapok vissza, amely az adott keresési feltételekkel nem üres.

searchresults = conn.search("dc=irf,dc=local", 2, "(telephoneNumber=666)", ["sn", "gn", "telephoneNumber"])
search
results
[{'sn': ['Swift'], 'givenName': ['Isaac'], 'telephoneNumber': ['707-8269-666']},
{'sn': ['Kirkham'], 'givenName': ['Jay'], 'telephoneNumber': ['614-3938-666']}]

jó pár másik eredmény

Gondoltam megkérdezem itt a dolgot, ha már az API dokumentáció nem túl segítőkész ilyen téren.

2 Answers

+2 votes
answered Mar 30, 2016 by nadudvarit (1,121 points)  

Long story short: a rendezéseket Pythonos házi feladatok esetén kliensoldalon végezzük. A feladatnak megfelelő LDAP-os szűrőfeltételeket és megfelelő kiindulási csomópontot (base DN) alkalmazva, a fájlba írás előtt rendezzük a adatszerkezetünket pythonos függvénnyel.

Kicsit bővebben: hogy szerveroldali rendezést tudjál végre hajtani, ahhoz

  1. az OpenLDAP szerver beállításai között engedélyezni kellene a Server Side Sorting kiegészítést.
  2. az szerverbe olyan LDAP sémákat kellene betölteni, amelyeknél a a különböző attribútumokra (pl. a vezetéknévre) be van állítva rendezés. Ezt tipikusan kézzel kell elvégezni, az OpenLDAP-pal mellékelt sémáknál szinte egyetlen attribútumhoz sincs megadva alapértelmezettként rendezési szabály.

A kiadott VM-en ezek egyike sincs elvégezve.

Ezért van az amit tapasztaltál, hogy a visszakapott ldapsearchiter (ami mellékesen egy kisebb bug, ugyanis rendezés esetén nem nyújt plusz funkcionalitást a sima keresés során visszakapott listához képest) objektumon for ciklussal végigiterálva (vagy egy list függvénynek paraméterként átadva) megkapod ugyan a találat elemeit, viszont rendezetlenül.

Az így érzett frusztráció -- vagyis hogy ott van egy rendezési opció, de a visszakapott adatod egyáltalán nem rendezett és erről te semmi visszajelzést nem kapsz senkitől -- teljesen jogos.
Elvárható lenne, hogy valaki jelezzen és az LDAP API-ban erre van is egy opció: függvény hívás esetén egy flagget beállítva megadható, hogy ha az adott LDAP controlt (jelenesetben a rendezést) a szerver nem tudja teljesíteni, akkor az adott operáció vagyis a keresés se hajtódjon végre, hanem hibával jelezzen vissza.
De ahogy azt már csak lenni szokott a szabvány és az implementáció nem mindig jár kéz a kézben: OpenLDAP esetén ha ezt az adott flaget igazra állítjuk hiába ha akár még képes is lenne a szerver a kiszolgálásra, ő ugyan azt a műveletet nem fogja végrehajtani (vagyis pontosabban a kérést már el se küldi a szervernek).

Akkor hát hogyan is érdemes bármilyen LDAP Controlt használni kliensoldalon? Ahogy a dokumentáció első bekezdése is említi a szerver root DSE-jének lekérésével ellenőrizni lehet, hogy az általunk használni kívánt control OID-je (egyedi azonosítója) szerepel-e supportedControls attribútum között. Ha igen akkor a szkriptünk további részében építhetünk erre a controlra, ha nem akkor más megoldás után kell néznünk.

+1 vote
answered Mar 30, 2016 by kovari (2,221 points)  

Kicsit játszadoztam most ezzel. Az a probléma, ahogy én látom, hogy az acquirenextpage() metódus minden esetben None-nal tér vissza, akkor is, ha elméletben találatokat kellett volna kapnunk.

Ha kicsit közelebbről megnézzük:

print(dir(searchresults))  
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', 
'__format__', '__ge__', '__getattribute__', '__gt__', 
'__hash__', '__init__', '__iter__', '__le__',     
'__len__', '__lt__', '__ne__', '__new__', '__next__', 
'__reduce__', '__reduce_ex__', '__repr__', 
'__setattr__', '__sizeof__', '__str__', 
'__subclasshook__', 'acquire_next_page']

látható, hogy például létezik len vagy next attribútum is. Ezeket kipróbálva:

print(searchresults.__len__())
11

látszik, hogy 11 találat van, vagy pedig a nextet használva:

print(searchresults.__next__())
{'sn': ['Swift'], 'telephoneNumber': ['707-8269-666'], 'givenName': ['Isaac']}

sikerült is kinyerni az első elemet.

Tehát ez elemek megvannak, azonban a rendezés továbbra sem sikerül :) Eddig jutottam.

Én egy kis bugra gyanakszom a háttérben, hiszen gyanús, hogy az acquirenextpage() mindig None. Amúgy nem várjuk el a szerveroldali rendezést, elég, ha a releváns attribútumokat leszűröd és azokat lokál rendezed. Főleg, hogy ez a funkció nem is igazán működik a jelek szerint.

...