/ manual.md
manual.md
  1  ---
  2  title: ISA - LDAP server
  3  author: René Češka
  4  date: 19.11.2023
  5  toc: true
  6  numbersections: true
  7  geometry: margin=2.5cm
  8  urlcolor: blue
  9  header-includes: |
 10      \usepackage{fancyhdr}
 11      \pagestyle{fancy}
 12      \rfoot{Page \thepage}
 13  ---
 14  
 15  
 16  
 17  # Teorie
 18  
 19  ## LDAP
 20  
 21  LDAP (Lightweight Directory Access Protocol) je mechanismus pro interakci s adresářovými servery. Je často využíván k autentizaci a ukládání informací o uživatelích, skupinách a aplikacích. LDAP server se dá ale využít i pro ukládání jiných informací. [1]
 22  
 23  Adresáře jsou ve stromových strukturách. Každá položka v adresáři má vlastní atributy, ke kterým lze přistupovat. [1]
 24  
 25  ## BER ANS.1
 26  
 27  LDAP používá pro komunikaci TLV kódování BER. Základní princip tohoto kódování je že každý datový typ se skládá ze tří částí. Tag, délka a data. Tag určuje typ dat, délka určuje délku dat a data jsou samotná data.
 28  BER obsahuje spoustu základních typů (int, boolean, sequence, enum, atd...) a také je ho možné rozšířit o typy specifické pro vlastní aplikaci.
 29  [2]
 30  
 31  ## Komunikace
 32  
 33  Veškeré zprávy jsou zabalené v tzv. obálce, která obsahuje MessageID. Toto MessageID slouží k identifikaci, na kterou zprávu server odpovídá, a veškeré zprávy serveru obsahují stejné MessageID jako zpráva, na kterou odpovídají. [4]
 34  
 35  Komunikace probíhá většinou pomocí TCP protokolu, ale je možné použít i UDP.
 36  
 37  ### LDAP princip vyhledávání
 38  
 39  Při vyhledávání, je klientem serveru zaslán dotaz. Tento dotaz obsahuje filter, doménu, volitelně přihlašovací údaje a další parametry.
 40  Server následně tento dotaz zpracuje a vrací jednotlivé položky co odpovídají dotazu. Odpověď dokončí odpovědí SearchResultDone, s kódem 0, který značí úspěšné dokončení vyhledávání, případně s jiným kódem, který značí chybu.  [4]
 41  
 42  ### LDAP princip přihlášení
 43  
 44  Kdykoli během komunikace může klient požádat o přihlášení. Přihlášení probíhá pomocí BIND_REQUEST, který obsahuje typ přihlášení a přihlašovací údaje. Server následně zpracuje tento požadavek a vrátí odpověď BIND_RESPONSE, která obsahuje kód, který značí úspěšné přihlášení, nebo chybu.  [4]
 45  
 46  ## Filtry
 47  
 48  V LDAP existují různé druhy filtrů, které se dají kombinovat a pomoci nich vyhledávat položky. Mezi filtry implementované v této aplikaci patří: Equality Match, Substrings, AND, OR, NOT. [4][3]
 49  
 50  Příklady filtrů:
 51  
 52  - Equality Match: (uid=xvesel38) - vyhledává uživatele s uid xvesel38
 53  - Substrings: (uid=\*vesel\*) - vyhledává uživatele jejichž uid obsahuje vesel
 54  - AND: (&(uid=xvesel38)(uid=\*vesel\*)) - vyhledává uživatele jejichž uid obsahuje vesel a je rovno xvesel38
 55  - OR: (|(uid=xvesel38)(uid=xvesel40)(uid=xvesel39)) - vyhledává uživatele jejichž uid je xvesel38, xvesel40 nebo xvesel39
 56  - NOT: (!(uid=xvesel38)) - vyhledává uživatele jejichž uid není xvesel38
 57  
 58  ## CSV
 59  
 60  CSV (Comma-separated values) je jednoduchý formát pro ukládání tabulkových dat. Jednotlivé řádky jsou odděleny znakem nového řádku a jednotlivé sloupce jsou odděleny čárkou případně jiným oddělovačem. [5]
 61  
 62  ## TCP
 63  
 64  TCP (Transmission Control Protocol) je spolehlivý protokol pro přenos dat. Zajišťuje, že data dorazí v pořádku a ve správném pořadí. Jedná se o proud dat. [6]
 65  
 66  # Použití
 67  
 68  ./isa-ldapserver {-p \<port\>} -f \<soubor\>
 69  
 70  
 71  Význam parametrů a jejich hodnot:
 72  
 73  -p \<port\>: Umožňuje specifikovat konkrétní port, na kterém začne server naslouchat požadavkům klientů. Výchozí hodnota čísla portu je 389.
 74  -f \<soubor\>: Cesta k textovému soubor ve formátu CSV.
 75  
 76  Aplikaci lze ukončit pomocí klávesové zkratky Ctrl+C (SIGINT).
 77  
 78  ## Sestavení
 79  
 80  Sestavení probíhá pomocí příkazu `make`. Výsledkem je spustitelný soubor `isa-ldapserver`. Veškeré zdrojové soubory jsou ve složce src, a hlavičkové soubory ve složce inc.
 81  Příkaz `make clean` smaže obj soubory a binárku.
 82  
 83  
 84  # Popis funkce aplikace
 85  
 86  Hlavní smyčka aplikace je v souboru server.cpp, ve kterém se prvně nastaví poslouchání na uživatelem zadaném portu. Následně aplikace čeká dokud nepřijde požadavek od ldap klienta. Následně dojde k forku, a v dceřiném procesu se zpracuje požadavek klienta.
 87  
 88  Na požadavek podproces reaguje dle jeho typu. Podporuje 3 druhy požadavků od klienta. Tyto požadavky jsou:
 89  
 90  - BIND_REQUEST
 91  - SEARCH_REQUEST
 92  - UNBIND_REQUEST
 93  
 94  ## BIND_REQUEST
 95  
 96  Bind request může přijít kdykoli a nemusí být prvním požadavkem. Zde se ověřuje jestli client žádá o správný typ přihlášení. Aplikace podporuje pouze simple bind, pokud klient zažádá o jiný způsob přihlášení, je mu vrácena chyba, a komunikace ukončena. Pokud je vše v pořádku, je klientovi vrácen úspěšný BIND_RESPONSE.
 97  
 98  ## SEARCH_REQUEST
 99  
100  Search request může přijít kdykoli a není třeba aby navazoval na bindrequest. Podproces následně zpracuje tento požadavek, vyhledá v databázi odpovídající záznamy a pomocí odpovědi SearchResultEntry je vrátí klientovi (na každý záznam se odesílá jedna SearchResultEntry).
101  Nakonec pošle SearchResultDone, kterým oznámí klientovi, že je vyhledávání dokončeno.
102  
103  ## UNBIND_REQUEST
104  
105  Jakmile příjde tento požadavek, podproces ukončí komunikaci s klientem a ukončí se.
106  
107  ## Ukončení
108  
109  Při zaslání SIGINT (CTRL + c) začne hlavní proces ukončovat všechny své podprocesy a následně se ukončí.
110  
111  
112  # Rozšíření oproti zadání
113  
114  ## Výběr atributů
115  
116  Aplikace podporuje možnost vybrat si jaké atributy chce uživatel vyhledat. V případě, že uživatel nevybere žádný atribut, jsou mu vráceny všechny atributy. Neexistující atributy jsou ignorovány.
117  
118  ## Notice of Disconnection
119  
120  Pokud dojde k chybě, která nejde oznámit přes odpovídající odpověď na požadavek, je klientovi oznámeno ukončení komunikace pomocí Notice of Disconnection.
121  
122  ## Přihlášení kdykoli
123  
124  Není třeba se přihlásit před bind requestem, ale je možné se přihlásit kdykoli během komunikace (LDAP3). Jelikož server podporuje jen podporuje jen simple bind, bez autentizace tak to nemá význam. Ale je možné potom velice jednoduše rozšířit aplikaci pro další typy autentizace.
125  
126  # Zdrojový kód
127  
128  Program byl psát v jazyku CPP s objektovým přístupem. Zdrojový kód byl dokumentován pomocí Doxygenu. Výsledná dokumentace je k dispozici v adresáři docs v souboru docs/refman.pdf.
129  
130  Zde je pouze stručný popis zajímavých tříd, jejich významu a použití.
131  
132  ## Popis důležitých souborů
133  
134  - server.h - obsahuje ldap server
135  - ldap_comunication.h - obsahuje třídy pro komunikaci s klientem
136  - DatabaseController.h - obsahuje třídy pro práci s csv databází
137  - BerObject.h - základní objekt pro BER typy
138    - BerXObject.h - objekt pro konkrétní BER typ, dědí z BerObject
139  - FilterObject.h - základní objekt pro LDAP filtry
140    - XFilterObject.h - objekt pro konkrétní LDAP filtr, dědí z FilterObject
141  
142  Podrobnější popis v souboru docs/refman.pdf.
143  
144  ## Třídy
145  
146  ## BerObject
147  
148  Je bázovým objektem pro objekty reprezentující BER struktury. Obsahuje základní metody pro ostatní objekty.
149  
150  Pomocí funkce `getBerRepresentation`  lze získat reprezentaci objektu v podobě BER bajtového pole. Které lze snadno odeslat klientovy.
151  Pro deserializaci je třeba využít funkce `ParseBerObject` která vrací ukazatel na nově vytvořený objekt.
152  
153  Z těchto objektů lze snadno vytvářet BER struktury a pracovat s nimy.
154  
155  ![BerObject](img/BerObject-UML.png)
156  
157  ## FilterObject
158  
159  Je bázovým objektem pro objekty reprezentující LDAP filtry. Jeho podtřídy jsou obsahují metody pro snadnou práci s nimy.
160  
161  ![FilterObject](img/FilterObject-UML.png)
162  
163  ## DatabaseController
164  
165  Třída DatabaseController slouží pro práci s csv databází. Obsahuje metody pro načítání řádků z databáze a vrací je v podobě objektů třídy DatabaseObject.
166  
167  ## DatabaseObject
168  
169  Třída DatabaseObject slouží pro reprezentaci řádku v databázi. Obsahuje metody pro získání hodnot atributů.
170  
171  # Testování
172  
173  ## Testovací prostředí
174  
175  - client
176    - program ldapsearch
177    - OS Manjaro Linux 23.0.4
178  - server
179    - program isa-ldapserver
180    - OS CentOS Linux 7
181  - databáze uživatelů
182    - soubor [ldap-lidi-ascii.csv](http://nes.fit.vutbr.cz/ivesely/ldap-lidi-ascii.csv)
183  - shell
184    - zsh 5.9
185  
186  ## Testovací scénáře
187  
188  ## Vyhledávání
189  
190  ### Vyhledávání všech uživatelů
191  
192  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(uid=x*)"`
193  
194  - Wireshark: nehlásil chybu v komunikaci
195  
196  Výsledek prošel dle očekávání a program vypsal všechny uživatele, jejichž uid začíná na x(kompletní soubor).
197  
198  ### Vyhledávání uživatele pokročilejší filty
199  
200  - příkaz:
201  
202  ```ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(&(|(uid=xves*)(cn=*imir))(email=*stud*))"```
203  
204  - Wireshark: nehlásil chybu v komunikaci
205  
206  Výsledek prošel dle očekávání a program vypsal všechny uživatele, jejichž uid začíná na xves, nebo cn obsahuje imir a email obsahuje stud.
207  
208  ### forkování procesů
209  
210  ### 10 klientů
211  
212  - příkaz: pro jednoho client `nc merlin.fit.vutbr.cz 10013`
213  
214  Výsledek prošel dle očekávání a clienti se mohli připojovat a odpojovat kdykoliv chtěli.
215  
216  ### Ukončení serveru při připojení několika clientů
217  
218  - příkaz: pro jednoho client `nc merlin.fit.vutbr.cz 10013`
219  
220  Výsledek prošel dle očekávání a server se ukončil a všechny své podprocesy zabil. Také klientům, kteří byli ještě připojeni odeslal Notice of Disconnection s chybovým kódem unavailable.
221  
222  ### IPV6 a IPV4
223  
224  Toto testování proběhlo pouze na lokálním stroji(Manjaro), jelikož nemám IPV6 připojení k dispozici.
225  
226  ### IPV6
227  
228  - příkaz: `ldapsearch -H ldap://\[::1\]:10013 -x "(uid=xvesel38)"`
229  - Wireshark: nehlásil chybu v komunikaci
230  Výsledek prošel dle očekávání a program vypsal uživatele s uid xvesel38.
231  
232  ### IPV4
233  
234  - příkaz: `ldapsearch -H ldap://127.0.0.1:10013 -x "(uid=xvesel38)"`
235  - Wireshark: nehlásil chybu v komunikaci
236  
237  Výsledek prošel dle očekávání a program vypsal uživatele s uid xvesel38.
238  
239  
240  ### Různé parametry
241  
242  ### špatné argumenty při spuštění serveru
243  
244  - příkaz: `./isa-ldapserver -p 10000000 -f ldap-lidi-ascii.csv`
245  - příkaz: `./isa-ldapserver -p 0 -f ldap-lidi-ascii.csv`
246  - příkaz: `./isa-ldapserver -p -f ldap-lidi-ascii.csv`
247  - příkaz: `./isa-ldapserver -f neexistujici_soubor`
248  
249  Při všech těchto příkazech došlo k ukončení serveru a vypsání chybového výstupu.
250  
251  ### ldapsearch bez -x (bez simple bind)
252  
253  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 "(uid=xvesel38)"`
254  - Wireshark: nehlásil chybu v komunikaci
255  
256  Výsledek dle očekávání vrátil `ldap_sasl_interactive_bind: No such object (32)` jelikož tato implementace ldap serveru nepodporuje presence filtry, takže ldapsearch nenašel žádný záznam o použitých technologiích k přihlášení a ukončil komunikaci, bez pokusu o přihlášení.
257  
258  ### size limit
259  
260  #### size limit 0
261  
262  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(uid=x*)" -z 0`
263  - Wireshark: nehlásil chybu v komunikaci
264  
265  Výsledek dle očekávání vrátil všechny uživatele, jelikož size limit byl nastaven na 0, a tedy deaktivován.
266  
267  #### size limit 1
268  
269  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(uid=x*)" -z 1`
270  - Wireshark: nehlásil chybu v komunikaci
271  
272  Výsledek dle očekávání vrátil pouze jednoho uživatele, jelikož size limit byl nastaven na 1.
273  
274  #### size limit 850
275  
276  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(uid=x*)" -z 850`
277  - Wireshark: nehlásil chybu v komunikaci
278  
279  Výsledek dle očekávání vrátil 850 uživatelů, jelikož size limit byl nastaven na 850.
280  
281  #### size limit 200000
282  
283  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(uid=x*)" -z 200000`
284  - Wireshark: nehlásil chybu v komunikaci
285  
286  Výsledek dle očekávání vrátil všechny uživatele, jelikož size limit byl nastaven na 200000, což je více než je počet uživatelů v databázi.
287  
288  ### Jednotlivé filtry
289  
290  ### Equality Match
291  
292  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(uid=xvesel38)"`
293  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(cn=Vesely Vladimir)"`
294  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(email=xvesel38@stud.fit.vutbr.cz)`
295  
296  - Wireshark: nehlásil chybu v komunikaci
297  
298  Výsledek prošel dle očekávání a program vypsal u všech příkazů následující výstup:
299  
300  ```
301  # extended LDIF
302  #
303  # LDAPv3
304  # base <> (default) with scope subtree
305  # filter: (uid=xvesel38)
306  # requesting: ALL
307  #
308  
309  #
310  dn: xvesel38
311  cn: Vesely Vladimir
312  email: xvesel38@stud.fit.vutbr.cz
313  uid: xvesel38
314  
315  # search result
316  search: 2
317  result: 0 Success
318  
319  # numResponses: 2
320  # numEntries: 1
321  
322  ```
323  
324  ### Substrings
325  
326  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(uid=*ve*el*)"`
327  
328  - Wireshark: nehlásil chybu v komunikaci
329  
330  Výsledek prošel dle očekávání a program vypsal všechny uživatele, jejichž uid obsahuje `ve` a `el`.
331  
332  ### AND
333  
334  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(&(uid=xve*)(uid=*sel38))"`
335  
336  - Wireshark: nehlásil chybu v komunikaci
337  
338  Výsledek prošel dle očekávání a program vypsal všechny uživatele s uid xvesel38.
339  
340  ```
341  # extended LDIF
342  #
343  # LDAPv3
344  # base <> (default) with scope subtree
345  # filter: (&(uid=xve*)(uid=*sel38))
346  # requesting: ALL
347  #
348  
349  #
350  dn: xvesel38
351  cn: Vesely Vladimir
352  email: xvesel38@stud.fit.vutbr.cz
353  uid: xvesel38
354  
355  # search result
356  search: 2
357  result: 0 Success
358  
359  # numResponses: 2
360  # numEntries: 1
361  
362  ```
363  
364  ### OR
365  
366  - příkaz: ```ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x \"(|(uid=xvesel38)(uid=xvesel40))" ```
367  - Wireshark: nehlásil chybu v komunikaci
368  
369  Výsledek prošel dle očekávání a program vypsal 3 uživatele s uid xvesel38, xvesel40.
370  
371  ```
372  # extended LDIF
373  #
374  # LDAPv3
375  # base <> (default) with scope subtree
376  # filter: (|(uid=xvesel38)(uid=xvesel40)(uid=xvesel39))
377  # requesting: ALL
378  #
379  
380  #
381  dn: xvesel40
382  cn: Vesely Ales
383  email: xvesel40@stud.fit.vutbr.cz
384  uid: xvesel40
385  
386  #
387  dn: xvesel38
388  cn: Vesely Vladimir
389  email: xvesel38@stud.fit.vutbr.cz
390  uid: xvesel38
391  
392  # search result
393  search: 2
394  result: 0 Success
395  
396  # numResponses: 4
397  # numEntries: 3
398  ```
399  
400  ### NOT
401  
402  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(|(!(uid=x*))(uid=xvesel38))"`
403  - Wireshark: nehlásil chybu v komunikaci
404  
405  Výsledek prošel dle očekávání a vypsal pouze jednoho uživatele s uid xvesel38.
406  
407  ```
408  # extended LDIF
409  #
410  # LDAPv3
411  # base <> (default) with scope subtree
412  # filter: (|(!(uid=x*))(uid=xvesel38))
413  # requesting: ALL
414  #
415  
416  #
417  dn: xvesel38
418  cn: Vesely Vladimir
419  email: xvesel38@stud.fit.vutbr.cz
420  uid: xvesel38
421  
422  # search result
423  search: 2
424  result: 0 Success
425  
426  # numResponses: 2
427  # numEntries: 1
428  
429  ```
430  
431  ### speciální případy
432  
433  ### Vyhledávání podle neexistujícího atributu
434  
435  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(uidd=xvesel38)"`
436  - WireShark: nehlásil chybu v komunikaci
437  
438  Výsledek dle očekávání neobsahoval žádné záznamy.
439  
440  ### Negace vyhledávání podle neexistujícího atributu
441  
442  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(!(uidd=xvesel38))"`
443  - WireShark: nehlásil chybu v komunikaci
444  
445  Výsledek dle očekávání neobsahoval žádné záznamy.
446  
447  ### Dlouhý filtr
448  
449  - příkaz: ```ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(|(uid=xvesel38)(uid=xvesel40))"```
450    - pozn: (uid=xvesel38)(uid=xvesel40) se 100 x opakovalo
451  
452  - WireShark: nehlásil chybu v komunikaci
453  
454  Výsledek dle očekávání vypsal uživatele s uid xvesel38, xvesel40, xvesel39.
455  
456  ### Dlouhý Equality Match
457  
458  - příkaz: ```ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(!(uid=dlouhyretezec))"```
459    - pozn: `dlouhyretezec` byl 1000 x znak `a`
460  - WireShark: nehlásil chybu v komunikaci
461  
462  Výsledek dle očekávání obsahoval všechny uživatele.
463  
464  ### Dlouhý Substrings
465  
466  - příkaz: ```ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(!(uid=*dlouhyretezec))"```
467    - pozn: `dlouhyretezec` byl 1000 x znak `a`
468  - WireShark: nehlásil chybu v komunikaci
469  
470  Výsledek dle očekávání obsahoval všechny uživatele.
471  
472  ## Porovnání s ldap od FIT VUT
473  
474  ### Vyhledávání všech uživatelů uživatele s uid xvesel38
475  
476  - příkaz: `ldapsearch -H ldap://ldap.fit.vutbr.cz:389 -x "(uid=xvesel92)" uid cn mail`
477  
478  Výstup:
479  
480  ```
481  # extended LDIF
482  #
483  # LDAPv3
484  # base <> (default) with scope subtree
485  # filter: (uid=xvesel92)
486  # requesting: uid cn mail
487  #
488  
489  # xvesel92, fit.vutbr.cz
490  dn: uid=xvesel92,dc=fit,dc=vutbr,dc=cz
491  cn:: VmVzZWzDvSBCb3Jpcw==
492  uid: xvesel92
493  mail: xvesel92@stud.fit.vutbr.cz
494  
495  # search result
496  search: 2
497  result: 0 Success
498  
499  # numResponses: 2
500  # numEntries: 1
501  
502  ```
503  
504  - příkaz: `ldapsearch -H ldap://merlin.fit.vutbr.cz:10013 -x "(uid=xvesel38)"`
505  
506  ```
507  # extended LDIF
508  #
509  # LDAPv3
510  # base <> (default) with scope subtree
511  # filter: (&(uid=xvesel38))
512  # requesting: ALL
513  #
514  
515  #
516  dn: xvesel38
517  cn: Vesely Vladimir
518  email: xvesel38@stud.fit.vutbr.cz
519  uid: xvesel38
520  
521  # search result
522  search: 2
523  result: 0 Success
524  
525  # numResponses: 2
526  # numEntries: 1
527  ```
528  
529  # Literatura
530  
531  - [1] WILSON, Neil. _LDAP_. Online. Dostupné z: [https://ldap.com/](https://ldap.com/). [cit. 2023-11-19].
532  - [2] WILSON, Neil. _LDAPv3 Wire Protocol Reference: The ASN.1 Basic Encoding Rules_ [online]. [cit. 2023-11-19]. Dostupné z: https://ldap.com/ldapv3-wire-protocol-reference-asn1-ber/
533  - [3] WAHL, M, T HOWES a S KILLE. Lightweight Directory Access Protocol (v3) [online]. 1997 [cit. 2023-11-19]. Dostupné z: https://datatracker.ietf.org/doc/html/rfc2251
534  - [4] WAHL, M, T HOWES a S KILLE. _RFC2251: Lightweight Directory Access Protocol (v3)_ [online]. 1997 [cit. 2023-11-19]. Dostupné z: https://datatracker.ietf.org/doc/html/rfc2251
535  - [5] SHAFRANOVICH, Y. _RFC4180: Common Format and MIME Type for Comma-Separated Values CSV Files_ [online]. 2005 [cit. 2023-11-19]. Dostupné z: https://datatracker.ietf.org/doc/html/rfc2251
536  - [6] RAY, M.D. _RFC793: TRANSMISSION CONTROL PROTOCOL_ [online]. 1997 [cit. 2023-11-19]. Dostupné z: https://tools.ietf.org/html/rfc793
537  - [7] WILSON, Neil. LDAP Filters [online]. [cit. 2023-11-19]. Dostupné z: https://ldap.com/ldap-filters/