0
# Core LDAP Operations
1
2
Essential LDAP functionality providing client configuration, connection management, entry operations, distinguished name handling, and URL parsing. These components form the foundation for all LDAP directory server interactions.
3
4
## Capabilities
5
6
### LDAP Client Configuration
7
8
Configures connections to LDAP directory servers with authentication mechanisms, TLS settings, and various connection options.
9
10
```python { .api }
11
class LDAPClient:
12
def __init__(self, url: Union[LDAPURL, str] = "ldap://", tls: bool = False) -> None:
13
"""
14
Initialize LDAP client with server URL and TLS configuration.
15
16
Parameters:
17
- url: LDAP URL (e.g., "ldap://localhost" or "ldaps://server.com")
18
- tls: Enable TLS connection
19
"""
20
21
def set_credentials(
22
self,
23
mechanism: str,
24
user: Optional[str] = None,
25
password: Optional[str] = None,
26
realm: Optional[str] = None,
27
authz_id: Optional[str] = None,
28
keytab: Optional[str] = None,
29
) -> None:
30
"""
31
Set authentication credentials and mechanism.
32
33
Parameters:
34
- mechanism: Authentication mechanism ("SIMPLE", "GSSAPI", "EXTERNAL", etc.)
35
- user: Username or bind DN for authentication
36
- password: Password for simple authentication
37
- realm: Kerberos realm
38
- authz_id: Authorization ID for EXTERNAL mechanism
39
- keytab: Path to Kerberos keytab file
40
"""
41
42
def connect(self, is_async: bool = False) -> LDAPConnection:
43
"""
44
Create connection to LDAP server.
45
46
Parameters:
47
- is_async: Whether to return async connection type
48
49
Returns:
50
LDAPConnection object for performing operations
51
"""
52
53
def set_raw_attributes(self, raw_list: List[str]) -> None:
54
"""
55
Configure attributes to be returned as raw bytes instead of strings.
56
57
Parameters:
58
- raw_list: List of attribute names to keep in binary format
59
"""
60
61
def set_cert_policy(self, policy: int) -> None:
62
"""Set certificate validation policy for TLS connections."""
63
64
def set_ca_cert(self, name: str) -> None:
65
"""Set CA certificate file path for TLS validation."""
66
67
def set_ca_cert_dir(self, path: str) -> None:
68
"""Set CA certificate directory path for TLS validation."""
69
70
def set_client_cert(self, name: str) -> None:
71
"""Set client certificate file path for TLS authentication."""
72
73
def set_client_key(self, name: str) -> None:
74
"""Set client private key file path for TLS authentication."""
75
76
def set_password_policy(self, ppolicy: bool) -> None:
77
"""Enable password policy control."""
78
79
def set_extended_dn(self, extdn_format: Optional[int]) -> None:
80
"""
81
Set extended DN format for Active Directory.
82
83
Parameters:
84
- extdn_format: Extended DN format (0=hex string, 1=standard string)
85
"""
86
87
def set_sd_flags(self, flags: Optional[int]) -> None:
88
"""
89
Set security descriptor flags for Windows AD.
90
91
Parameters:
92
- flags: Security descriptor control flags
93
"""
94
95
def set_auto_page_acquire(self, val: bool) -> None:
96
"""Enable automatic page size acquisition for paged searches."""
97
98
def set_ignore_referrals(self, val: bool) -> None:
99
"""Set whether to ignore LDAP referrals."""
100
101
def set_server_chase_referrals(self, val: bool) -> None:
102
"""Set whether server should chase referrals."""
103
104
def set_managedsait(self, val: bool) -> None:
105
"""Set ManageDsaIT control for directory operations."""
106
107
def get_rootDSE(self) -> Optional["LDAPEntry"]:
108
"""
109
Get root DSE entry containing server capabilities.
110
111
Returns:
112
LDAPEntry with root DSE information or None if unavailable
113
"""
114
115
@property
116
def url(self) -> LDAPURL:
117
"""LDAP URL configuration."""
118
119
@property
120
def mechanism(self) -> str:
121
"""Authentication mechanism."""
122
123
@property
124
def tls(self) -> bool:
125
"""TLS connection flag."""
126
127
@property
128
def password_policy(self) -> bool:
129
"""Password policy control status."""
130
131
@property
132
def extended_dn_format(self) -> Optional[int]:
133
"""Extended DN format setting."""
134
135
@property
136
def sd_flags(self) -> Optional[int]:
137
"""Security descriptor flags."""
138
139
@property
140
def auto_page_acquire(self) -> bool:
141
"""Automatic page acquisition status."""
142
143
@property
144
def ignore_referrals(self) -> bool:
145
"""Ignore referrals setting."""
146
147
@property
148
def server_chase_referrals(self) -> bool:
149
"""Server chase referrals setting."""
150
151
@property
152
def managedsait(self) -> bool:
153
"""ManageDsaIT control setting."""
154
```
155
156
### LDAP Connection Operations
157
158
Provides all LDAP directory operations including search, add, modify, delete, and rename operations.
159
160
```python { .api }
161
class LDAPConnection:
162
def search(
163
self,
164
base: Optional[Union[str, LDAPDN]] = None,
165
scope: Optional[Union[LDAPSearchScope, int]] = None,
166
filter_exp: Optional[str] = None,
167
attrlist: Optional[List[str]] = None,
168
timeout: Optional[float] = None,
169
sizelimit: int = 0,
170
attrsonly: bool = False,
171
sort_order: Optional[List[str]] = None,
172
page_size: int = 0,
173
) -> List[LDAPEntry]:
174
"""
175
Search LDAP directory for entries matching criteria.
176
177
Parameters:
178
- base: Base DN to search from
179
- scope: Search scope (BASE=0, ONELEVEL=1, SUBTREE=2)
180
- filter_exp: LDAP filter expression (e.g., "(objectClass=person)")
181
- attrlist: List of attributes to retrieve (None for all)
182
- timeout: Operation timeout in seconds
183
- sizelimit: Maximum number of entries to return
184
- attrsonly: Return attribute names only, no values
185
- sort_order: List of attributes for server-side sorting
186
- page_size: Enable paged results with specified page size
187
188
Returns:
189
List of LDAPEntry objects matching the search criteria
190
"""
191
192
def add(self, entry: LDAPEntry, timeout: Optional[float] = None) -> None:
193
"""
194
Add new entry to LDAP directory.
195
196
Parameters:
197
- entry: LDAPEntry object with DN and attributes
198
- timeout: Operation timeout in seconds
199
"""
200
201
def modify(self, entry: LDAPEntry, timeout: Optional[float] = None) -> None:
202
"""
203
Modify existing entry in LDAP directory.
204
205
Parameters:
206
- entry: LDAPEntry object with modifications
207
- timeout: Operation timeout in seconds
208
"""
209
210
def delete(
211
self,
212
dname: Union[str, LDAPDN],
213
timeout: Optional[float] = None,
214
recursive: bool = False,
215
) -> None:
216
"""
217
Delete entry from LDAP directory.
218
219
Parameters:
220
- dname: Distinguished name of entry to delete
221
- timeout: Operation timeout in seconds
222
- recursive: Delete entry and all children
223
"""
224
225
def rename(
226
self,
227
dn: Union[str, LDAPDN],
228
newrdn: str,
229
new_superior: Optional[Union[str, LDAPDN]] = None,
230
delete_old_rdn: bool = True,
231
timeout: Optional[float] = None,
232
) -> None:
233
"""
234
Rename/move entry in LDAP directory.
235
236
Parameters:
237
- dn: Current distinguished name
238
- newrdn: New relative distinguished name
239
- new_superior: New parent DN (for move operation)
240
- delete_old_rdn: Remove old RDN attribute values
241
- timeout: Operation timeout in seconds
242
"""
243
244
def modify_password(
245
self,
246
user: Optional[Union[str, LDAPDN]] = None,
247
new_password: Optional[str] = None,
248
old_password: Optional[str] = None,
249
timeout: Optional[float] = None,
250
) -> str:
251
"""
252
Modify user password using LDAP password modify extended operation.
253
254
Parameters:
255
- user: User DN (None for current authenticated user)
256
- new_password: New password (None for server-generated)
257
- old_password: Current password for verification
258
- timeout: Operation timeout in seconds
259
260
Returns:
261
New password if server-generated
262
"""
263
264
def abandon(self, msg_id: int) -> None:
265
"""
266
Abandon ongoing LDAP operation.
267
268
Parameters:
269
- msg_id: Message ID of operation to abandon
270
"""
271
272
def whoami(self, timeout: Optional[float] = None) -> str:
273
"""
274
Execute LDAP "Who Am I?" extended operation to get current identity.
275
276
Parameters:
277
- timeout: Operation timeout in seconds
278
279
Returns:
280
Authorization identity string
281
"""
282
283
def paged_search(
284
self,
285
base: Optional[Union[str, LDAPDN]] = None,
286
scope: Optional[Union[LDAPSearchScope, int]] = None,
287
filter_exp: Optional[str] = None,
288
attrlist: Optional[List[str]] = None,
289
timeout: Optional[float] = None,
290
page_size: int = 1,
291
sort_order: Optional[List[str]] = None,
292
) -> "LDAPSearchIter":
293
"""
294
Perform paged search returning an iterator for large result sets.
295
296
Parameters:
297
- base: Base DN to search from
298
- scope: Search scope (BASE=0, ONELEVEL=1, SUBTREE=2)
299
- filter_exp: LDAP filter expression
300
- attrlist: List of attributes to retrieve
301
- timeout: Operation timeout in seconds
302
- page_size: Number of entries per page
303
- sort_order: List of attributes for server-side sorting
304
305
Returns:
306
LDAPSearchIter object for iterating through paged results
307
"""
308
309
def virtual_list_search(
310
self,
311
base: Optional[Union[str, LDAPDN]] = None,
312
scope: Optional[Union[LDAPSearchScope, int]] = None,
313
filter_exp: Optional[str] = None,
314
attrlist: Optional[List[str]] = None,
315
timeout: Optional[float] = None,
316
offset: int = 1,
317
before_count: int = 0,
318
after_count: int = 0,
319
est_list_count: int = 0,
320
attrvalue: Optional[str] = None,
321
sort_order: Optional[List[str]] = None,
322
) -> "LDAPSearchIter":
323
"""
324
Perform virtual list view search for efficient browsing of large sorted lists.
325
326
Parameters:
327
- base: Base DN to search from
328
- scope: Search scope
329
- filter_exp: LDAP filter expression
330
- attrlist: List of attributes to retrieve
331
- timeout: Operation timeout in seconds
332
- offset: Target entry position in virtual list
333
- before_count: Number of entries to return before target
334
- after_count: Number of entries to return after target
335
- est_list_count: Estimated total list size
336
- attrvalue: Target attribute value for positioning
337
- sort_order: Required sort order for virtual list view
338
339
Returns:
340
LDAPSearchIter object for virtual list results
341
"""
342
343
def close(self) -> None:
344
"""Close LDAP connection and free resources."""
345
346
def open(self, timeout: Optional[float] = None) -> "LDAPConnection":
347
"""
348
Open connection to LDAP server.
349
350
Parameters:
351
- timeout: Connection timeout in seconds
352
353
Returns:
354
Self for method chaining
355
"""
356
357
@property
358
def is_closed(self) -> bool:
359
"""Connection closed status."""
360
361
@property
362
def tls_inuse(self) -> bool:
363
"""TLS in use status."""
364
```
365
366
### LDAP Entry Handling
367
368
Dictionary-like interface for LDAP entries with automatic change tracking and type conversion.
369
370
```python { .api }
371
class LDAPEntry:
372
def __init__(self, dn: Union[str, LDAPDN], conn: Optional[LDAPConnection] = None) -> None:
373
"""
374
Initialize LDAP entry.
375
376
Parameters:
377
- dn: Distinguished name of the entry
378
- conn: LDAP connection for automatic synchronization
379
"""
380
381
def __getitem__(self, key: str) -> LDAPValueList:
382
"""Get attribute value list by name."""
383
384
def __setitem__(self, key: str, value: Any) -> None:
385
"""Set attribute value(s)."""
386
387
def __delitem__(self, key: str) -> None:
388
"""Delete attribute."""
389
390
def __contains__(self, key: str) -> bool:
391
"""Check if attribute exists."""
392
393
def __iter__(self):
394
"""Iterate over attribute names."""
395
396
def keys(self):
397
"""Get attribute names."""
398
399
def values(self):
400
"""Get attribute value lists."""
401
402
def items(self):
403
"""Get (attribute, value_list) pairs."""
404
405
def get(self, key: str, default=None):
406
"""Get attribute with default value."""
407
408
def update(self, other: dict) -> None:
409
"""Update entry with dictionary of attributes."""
410
411
def clear(self) -> None:
412
"""Remove all attributes except DN."""
413
414
def modify(self, timeout: Optional[float] = None) -> None:
415
"""
416
Save changes to LDAP directory (requires connection).
417
418
Parameters:
419
- timeout: Operation timeout in seconds
420
"""
421
422
def rename(
423
self,
424
newdn: Union[str, LDAPDN],
425
delete_old_rdn: bool = True,
426
timeout: Optional[float] = None,
427
) -> None:
428
"""
429
Rename entry in LDAP directory (requires connection).
430
431
Parameters:
432
- newdn: New distinguished name
433
- delete_old_rdn: Remove old RDN attributes
434
- timeout: Operation timeout in seconds
435
"""
436
437
def delete(self, timeout: Optional[float] = None) -> None:
438
"""
439
Delete entry from LDAP directory (requires connection).
440
441
Parameters:
442
- timeout: Operation timeout in seconds
443
"""
444
445
@property
446
def dn(self) -> LDAPDN:
447
"""Distinguished name of the entry."""
448
449
@property
450
def connection(self) -> Optional[LDAPConnection]:
451
"""Associated LDAP connection."""
452
453
@property
454
def changes(self) -> dict:
455
"""Dictionary of pending changes."""
456
```
457
458
### Distinguished Name Handling
459
460
Parse, manipulate, and validate LDAP distinguished names with support for escaping and comparison.
461
462
```python { .api }
463
class LDAPDN:
464
def __init__(self, dnstr: str) -> None:
465
"""
466
Initialize distinguished name from string.
467
468
Parameters:
469
- dnstr: DN string (e.g., "cn=user,ou=people,dc=example,dc=com")
470
"""
471
472
def __str__(self) -> str:
473
"""Convert to string representation."""
474
475
def __eq__(self, other) -> bool:
476
"""Compare DNs for equality."""
477
478
def __getitem__(self, idx: int) -> str:
479
"""Get RDN component by index."""
480
481
def __len__(self) -> int:
482
"""Get number of RDN components."""
483
484
def __contains__(self, item: Union[str, "LDAPDN"]) -> bool:
485
"""Check if DN contains another DN or RDN."""
486
487
def __add__(self, other: Union[str, "LDAPDN"]) -> "LDAPDN":
488
"""Concatenate DNs."""
489
490
@property
491
def rdns(self) -> List[str]:
492
"""List of relative distinguished name components."""
493
494
def ancestors(self) -> List["LDAPDN"]:
495
"""Get list of ancestor DNs."""
496
497
def is_valid(self) -> bool:
498
"""Validate DN syntax."""
499
500
@staticmethod
501
def escape_attribute_value(value: str) -> str:
502
"""
503
Escape special characters in attribute values.
504
505
Parameters:
506
- value: Attribute value to escape
507
508
Returns:
509
Escaped attribute value
510
"""
511
```
512
513
### LDAP URL Handling
514
515
Parse and construct LDAP URLs with support for all standard components including host, port, base DN, scope, filter, and attributes.
516
517
```python { .api }
518
class LDAPURL:
519
def __init__(self, url: str) -> None:
520
"""
521
Initialize LDAP URL from string.
522
523
Parameters:
524
- url: LDAP URL string (e.g., "ldap://server.com:389/dc=example,dc=com?cn,sn?sub?(objectClass=person)")
525
"""
526
527
def __str__(self) -> str:
528
"""Convert to URL string."""
529
530
def __eq__(self, other) -> bool:
531
"""Compare URLs for equality."""
532
533
@property
534
def scheme(self) -> str:
535
"""URL scheme (ldap, ldaps, ldapi)."""
536
537
@property
538
def host(self) -> str:
539
"""LDAP server hostname."""
540
541
@property
542
def port(self) -> int:
543
"""LDAP server port number."""
544
545
@property
546
def basedn(self) -> Optional[LDAPDN]:
547
"""Base DN for operations."""
548
549
@property
550
def attributes(self) -> List[str]:
551
"""List of attributes to retrieve."""
552
553
@property
554
def scope(self) -> Optional[LDAPSearchScope]:
555
"""Search scope."""
556
557
@property
558
def scope_num(self) -> int:
559
"""Search scope as integer."""
560
561
@property
562
def filter_exp(self) -> Optional[str]:
563
"""LDAP filter expression."""
564
565
@property
566
def extensions(self) -> Optional[dict]:
567
"""URL extensions dictionary."""
568
569
def get_address(self) -> str:
570
"""Get server address (host:port)."""
571
572
@classmethod
573
def from_config(
574
cls,
575
host: str,
576
port: int = 389,
577
basedn: Optional[str] = None,
578
**kwargs
579
) -> "LDAPURL":
580
"""
581
Create LDAP URL from configuration parameters.
582
583
Parameters:
584
- host: LDAP server hostname
585
- port: LDAP server port
586
- basedn: Base DN string
587
- **kwargs: Additional URL components
588
589
Returns:
590
LDAPURL object
591
"""
592
```
593
594
### Value Lists
595
596
Specialized list for LDAP attribute values with change tracking and type handling.
597
598
```python { .api }
599
class LDAPValueList(list):
600
def __init__(self, items=None) -> None:
601
"""
602
Initialize value list with optional items.
603
604
Parameters:
605
- items: Initial list of values
606
"""
607
608
def append(self, item: Any) -> None:
609
"""Add value to list."""
610
611
def extend(self, items) -> None:
612
"""Add multiple values to list."""
613
614
def insert(self, index: int, item: Any) -> None:
615
"""Insert value at specific index."""
616
617
def remove(self, item: Any) -> None:
618
"""Remove first occurrence of value."""
619
620
def clear(self) -> None:
621
"""Remove all values."""
622
623
@property
624
def added(self) -> set:
625
"""Set of values marked as added."""
626
627
@property
628
def deleted(self) -> set:
629
"""Set of values marked as deleted."""
630
631
@property
632
def status(self) -> int:
633
"""Change status (0=unchanged, 1=added, 2=deleted, 3=replaced)."""
634
```
635
636
### Search Iterator
637
638
Iterator for handling paged search results and large result sets.
639
640
```python { .api }
641
class LDAPSearchIter:
642
def __init__(self, conn: LDAPConnection, base: str, scope: int, **kwargs) -> None:
643
"""
644
Initialize search iterator.
645
646
Parameters:
647
- conn: LDAP connection object
648
- base: Base DN for search
649
- scope: Search scope
650
- **kwargs: Additional search parameters
651
"""
652
653
def __iter__(self) -> "LDAPSearchIter":
654
"""Return iterator object."""
655
656
def __next__(self) -> LDAPEntry:
657
"""Get next entry from search results."""
658
659
def acquire_next_page(self) -> None:
660
"""Manually acquire next page of results."""
661
662
@property
663
def cookie(self) -> Optional[bytes]:
664
"""Paging cookie for server-side state."""
665
666
@property
667
def estimated_list_count(self) -> int:
668
"""Estimated total count for virtual list view."""
669
```
670
671
### LDAP Reference
672
673
Handles LDAP referrals returned by directory servers.
674
675
```python { .api }
676
class LDAPReference:
677
def __init__(self, client: LDAPClient, references: List[Union[str, LDAPURL]]) -> None:
678
"""
679
Initialize LDAP reference object.
680
681
Parameters:
682
- client: LDAP client for connection configuration
683
- references: List of referral URLs
684
"""
685
686
@property
687
def client(self) -> LDAPClient:
688
"""LDAP client configuration."""
689
690
@property
691
def references(self) -> List[LDAPURL]:
692
"""List of referral LDAP URLs."""
693
```
694
695
## Usage Examples
696
697
### Basic LDAP Operations
698
699
```python
700
from bonsai import LDAPClient, LDAPEntry, LDAPDN
701
702
# Configure client
703
client = LDAPClient("ldap://localhost:389")
704
client.set_credentials("SIMPLE", user="cn=admin,dc=example,dc=com", password="secret")
705
706
# Connect and search
707
with client.connect() as conn:
708
# Search for all person entries
709
results = conn.search(
710
"dc=example,dc=com",
711
2, # SUBTREE scope
712
"(objectClass=person)",
713
["cn", "sn", "mail"]
714
)
715
716
for entry in results:
717
print(f"Found: {entry.dn}")
718
print(f"Name: {entry['cn'][0]}")
719
if 'mail' in entry:
720
print(f"Email: {entry['mail'][0]}")
721
```
722
723
### Creating and Modifying Entries
724
725
```python
726
# Create new entry
727
new_user = LDAPEntry("cn=jdoe,ou=people,dc=example,dc=com")
728
new_user['objectClass'] = ['person', 'organizationalPerson', 'inetOrgPerson']
729
new_user['cn'] = 'jdoe'
730
new_user['sn'] = 'Doe'
731
new_user['givenName'] = 'John'
732
new_user['mail'] = 'jdoe@example.com'
733
734
with client.connect() as conn:
735
# Add to directory
736
conn.add(new_user)
737
738
# Modify existing entry
739
user = conn.search("cn=jdoe,ou=people,dc=example,dc=com", 0)[0] # BASE scope
740
user['title'] = 'Software Engineer'
741
user['telephoneNumber'] = '+1-555-0123'
742
conn.modify(user)
743
744
# Delete entry
745
conn.delete("cn=jdoe,ou=people,dc=example,dc=com")
746
```
747
748
### Distinguished Name Operations
749
750
```python
751
from bonsai import LDAPDN
752
753
# Parse DN
754
dn = LDAPDN("cn=John Doe,ou=people,dc=example,dc=com")
755
print(f"RDNs: {dn.rdns}") # ['cn=John Doe', 'ou=people', 'dc=example', 'dc=com']
756
print(f"Length: {len(dn)}") # 4
757
758
# Check containment
759
base_dn = LDAPDN("dc=example,dc=com")
760
print(base_dn in dn) # True
761
762
# Get ancestors
763
ancestors = dn.ancestors()
764
for ancestor in ancestors:
765
print(ancestor) # ou=people,dc=example,dc=com, then dc=example,dc=com, then dc=com
766
```