0
# Extended Operations
1
2
Extended operations for specific LDAP server implementations including Microsoft Active Directory, Novell eDirectory, and standard RFC extensions.
3
4
## Capabilities
5
6
### Standard Extended Operations
7
8
RFC-standard extended operations supported by most LDAP servers.
9
10
```python { .api }
11
# Standard extended operations available on connection.extend.standard
12
13
def who_am_i(self, controls=None):
14
"""
15
WHO AM I extended operation (RFC 4532).
16
17
Retrieves the authorization identity associated with the connection.
18
19
Args:
20
controls (list, optional): LDAP controls
21
22
Returns:
23
str: Authorization identity or None if not supported
24
"""
25
26
def modify_password(self, user, old_password, new_password, hash_algorithm=None,
27
salt=None, controls=None):
28
"""
29
Password Modify extended operation (RFC 3062).
30
31
Modify user password with optional hashing.
32
33
Args:
34
user (str): User DN whose password to change
35
old_password (str): Current password
36
new_password (str): New password
37
hash_algorithm (str, optional): Hash algorithm (HASHED_SHA, HASHED_MD5, etc.)
38
salt (bytes, optional): Salt for hashed password
39
controls (list, optional): LDAP controls
40
41
Returns:
42
bool: True if password changed successfully
43
"""
44
45
def paged_search(self, search_base, search_filter, search_scope=SUBTREE,
46
dereference_aliases=DEREF_ALWAYS, attributes=None,
47
size_limit=0, time_limit=0, types_only=False,
48
get_operational_attributes=False, controls=None,
49
paged_size=1000, paged_criticality=False):
50
"""
51
Paged search helper for large result sets.
52
53
Args:
54
search_base (str): Search base DN
55
search_filter (str): LDAP search filter
56
search_scope (str): Search scope
57
dereference_aliases (str): Alias dereferencing
58
attributes (list, optional): Attributes to retrieve
59
size_limit (int): Size limit per page
60
time_limit (int): Time limit in seconds
61
types_only (bool): Return types only
62
get_operational_attributes (bool): Include operational attributes
63
controls (list, optional): Additional LDAP controls
64
paged_size (int): Page size for results
65
paged_criticality (bool): Paged control criticality
66
67
Returns:
68
generator: Generator yielding Entry objects
69
"""
70
71
def persistent_search(self, search_base, search_filter, search_scope=SUBTREE,
72
dereference_aliases=DEREF_ALWAYS, attributes=None,
73
get_operational_attributes=False, controls=None,
74
changes_only=True, events_type=None,
75
notification_changes=True):
76
"""
77
Persistent Search extended operation (RFC 3673 draft).
78
79
Monitor directory changes in real-time.
80
81
Args:
82
search_base (str): Search base DN
83
search_filter (str): LDAP search filter
84
search_scope (str): Search scope
85
dereference_aliases (str): Alias dereferencing
86
attributes (list, optional): Attributes to monitor
87
get_operational_attributes (bool): Include operational attributes
88
controls (list, optional): LDAP controls
89
changes_only (bool): Return only changes, not initial entries
90
events_type (list, optional): Types of changes to monitor
91
notification_changes (bool): Include change notification info
92
93
Returns:
94
generator: Generator yielding change notifications
95
"""
96
```
97
98
### Microsoft Active Directory Extended Operations
99
100
Extended operations specific to Microsoft Active Directory.
101
102
```python { .api }
103
# Microsoft AD extended operations available on connection.extend.microsoft
104
105
def dir_sync(self, sync_base, sync_filter='(objectclass=*)', attributes=ALL_ATTRIBUTES, cookie=None,
106
object_security=False, ancestors_first=True, public_data_only=False,
107
incremental_values=True, max_length=2147483647, hex_guid=False):
108
"""
109
DirSync extended operation for Active Directory synchronization.
110
111
Efficiently synchronize directory changes from Active Directory.
112
113
Args:
114
sync_base (str): Base DN for synchronization
115
sync_filter (str): LDAP filter for objects to sync (default: '(objectclass=*)')
116
attributes (list): Attributes to synchronize (default: ALL_ATTRIBUTES)
117
cookie (bytes, optional): Synchronization cookie from previous sync
118
object_security (bool): Include object security information (default: False)
119
ancestors_first (bool): Return parent objects before children (default: True)
120
public_data_only (bool): Return only public data (default: False)
121
incremental_values (bool): Return incremental value changes (default: True)
122
max_length (int): Maximum response length (default: 2147483647)
123
hex_guid (bool): Return GUIDs in hexadecimal format (default: False)
124
125
Returns:
126
dict: Sync results with entries and new cookie
127
"""
128
129
def modify_password(self, user, new_password, old_password=None, controls=None):
130
"""
131
Active Directory password change operation.
132
133
Change user password using AD-specific method.
134
135
Args:
136
user (str): User DN whose password to change
137
new_password (str): New password
138
old_password (str, optional): Current password (required for user self-change)
139
controls (list, optional): LDAP controls
140
141
Returns:
142
bool: True if password changed successfully
143
"""
144
```
145
146
### Novell eDirectory Extended Operations
147
148
Extended operations specific to Novell eDirectory.
149
150
```python { .api }
151
# Novell eDirectory extended operations available on connection.extend.novell
152
153
def get_bind_dn(self, controls=None):
154
"""
155
Get Bind DN extended operation.
156
157
Retrieve the DN used for binding to eDirectory.
158
159
Args:
160
controls (list, optional): LDAP controls
161
162
Returns:
163
str: Bind DN or None if operation fails
164
"""
165
166
def get_universal_password(self, user, controls=None):
167
"""
168
Get Universal Password extended operation.
169
170
Retrieve user's universal password from eDirectory.
171
172
Args:
173
user (str): User DN
174
controls (list, optional): LDAP controls
175
176
Returns:
177
str: Universal password or None if not available
178
"""
179
180
def set_universal_password(self, user, new_password, controls=None):
181
"""
182
Set Universal Password extended operation.
183
184
Set user's universal password in eDirectory.
185
186
Args:
187
user (str): User DN
188
new_password (str): New universal password
189
controls (list, optional): LDAP controls
190
191
Returns:
192
bool: True if password set successfully
193
"""
194
195
def list_replicas(self, server_dn, controls=None):
196
"""
197
List Replicas extended operation.
198
199
List replicas for eDirectory server.
200
201
Args:
202
server_dn (str): Server DN
203
controls (list, optional): LDAP controls
204
205
Returns:
206
list: List of replica information
207
"""
208
209
def partition_entry_count(self, partition_dn, controls=None):
210
"""
211
Get Partition Entry Count extended operation.
212
213
Get number of entries in eDirectory partition.
214
215
Args:
216
partition_dn (str): Partition DN
217
controls (list, optional): LDAP controls
218
219
Returns:
220
int: Number of entries in partition
221
"""
222
223
def replica_info(self, server_dn, partition_dn, controls=None):
224
"""
225
Get Replica Info extended operation.
226
227
Get replica information for specific partition.
228
229
Args:
230
server_dn (str): Server DN
231
partition_dn (str): Partition DN
232
controls (list, optional): LDAP controls
233
234
Returns:
235
dict: Replica information
236
"""
237
238
def start_transaction(self, controls=None):
239
"""
240
Start Transaction extended operation.
241
242
Begin eDirectory transaction for atomic operations.
243
244
Args:
245
controls (list, optional): LDAP controls
246
247
Returns:
248
bytes: Transaction ID for subsequent operations
249
"""
250
251
def end_transaction(self, commit=True, controls=None):
252
"""
253
End Transaction extended operation.
254
255
Commit or abort eDirectory transaction.
256
257
Args:
258
commit (bool): True to commit, False to abort
259
controls (list, optional): LDAP controls
260
261
Returns:
262
bool: True if transaction ended successfully
263
"""
264
265
def add_members_to_groups(self, members, groups, fix=True, transaction=True):
266
"""
267
Add Members to Groups extended operation.
268
269
Efficiently add multiple members to multiple groups.
270
271
Args:
272
members (list): List of member DNs
273
groups (list): List of group DNs
274
fix (bool): Fix membership inconsistencies
275
transaction (bool): Use transaction for atomic operation
276
277
Returns:
278
bool: True if operation successful
279
"""
280
281
def remove_members_from_groups(self, members, groups, fix=True, transaction=True):
282
"""
283
Remove Members from Groups extended operation.
284
285
Efficiently remove multiple members from multiple groups.
286
287
Args:
288
members (list): List of member DNs
289
groups (list): List of group DNs
290
fix (bool): Fix membership inconsistencies
291
transaction (bool): Use transaction for atomic operation
292
293
Returns:
294
bool: True if operation successful
295
"""
296
297
def check_groups_memberships(self, members, groups, fix=False, transaction=True):
298
"""
299
Check Groups Memberships extended operation.
300
301
Check membership relationships between members and groups.
302
303
Args:
304
members (list): List of member DNs
305
groups (list): List of group DNs
306
fix (bool): Fix membership inconsistencies if found
307
transaction (bool): Use transaction for atomic operation
308
309
Returns:
310
dict: Membership check results
311
"""
312
```
313
314
## Usage Examples
315
316
### Standard Extended Operations
317
318
```python
319
import ldap3
320
321
server = ldap3.Server('ldap://ldap.example.com')
322
conn = ldap3.Connection(server, 'cn=admin,dc=example,dc=com', 'password', auto_bind=True)
323
324
# WHO AM I operation
325
identity = conn.extend.standard.who_am_i()
326
print(f"Current identity: {identity}")
327
328
# Password modification
329
result = conn.extend.standard.modify_password(
330
user='cn=john,ou=people,dc=example,dc=com',
331
old_password='oldpass',
332
new_password='newpass123',
333
hash_algorithm=ldap3.HASHED_SHA
334
)
335
if result:
336
print("Password changed successfully")
337
```
338
339
### Paged Search with Standard Extensions
340
341
```python
342
# Large result set with paged search
343
search_base = 'dc=example,dc=com'
344
search_filter = '(objectClass=person)'
345
346
# Paged search returns generator
347
entries = conn.extend.standard.paged_search(
348
search_base=search_base,
349
search_filter=search_filter,
350
search_scope=ldap3.SUBTREE,
351
attributes=['cn', 'mail'],
352
paged_size=100
353
)
354
355
# Process all results regardless of size
356
count = 0
357
for entry in entries:
358
print(f"Found: {entry.cn}")
359
count += 1
360
361
print(f"Total entries processed: {count}")
362
```
363
364
### Persistent Search for Real-time Monitoring
365
366
```python
367
# Monitor directory changes in real-time
368
try:
369
changes = conn.extend.standard.persistent_search(
370
search_base='ou=people,dc=example,dc=com',
371
search_filter='(objectClass=person)',
372
attributes=['cn', 'mail', 'telephoneNumber'],
373
changes_only=True
374
)
375
376
print("Monitoring directory changes... (Press Ctrl+C to stop)")
377
378
for change in changes:
379
change_type = change.get('change_type', 'unknown')
380
entry_dn = change.get('dn', 'unknown')
381
print(f"Change detected: {change_type} on {entry_dn}")
382
383
if 'attributes' in change:
384
for attr, values in change['attributes'].items():
385
print(f" {attr}: {values}")
386
387
except KeyboardInterrupt:
388
print("Monitoring stopped")
389
except ldap3.LDAPExtensionError as e:
390
print(f"Persistent search not supported: {e}")
391
```
392
393
### Microsoft Active Directory DirSync
394
395
```python
396
# Active Directory synchronization
397
server = ldap3.Server('ldap://dc.company.com')
398
conn = ldap3.Connection(server, 'DOMAIN\\syncuser', 'password', auto_bind=True)
399
400
# Initial sync
401
sync_result = conn.extend.microsoft.dir_sync(
402
sync_base='dc=company,dc=com',
403
sync_filter='(objectClass=user)',
404
sync_attributes=['cn', 'mail', 'whenChanged', 'objectGUID'],
405
object_security=False,
406
incremental_values=True
407
)
408
409
print(f"Initial sync found {len(sync_result['entries'])} entries")
410
411
# Save cookie for next sync
412
sync_cookie = sync_result['cookie']
413
414
# Later incremental sync
415
incremental_result = conn.extend.microsoft.dir_sync(
416
sync_base='dc=company,dc=com',
417
sync_filter='(objectClass=user)',
418
sync_attributes=['cn', 'mail', 'whenChanged', 'objectGUID'],
419
cookie=sync_cookie,
420
incremental_values=True
421
)
422
423
print(f"Incremental sync found {len(incremental_result['entries'])} changes")
424
```
425
426
### Active Directory Password Management
427
428
```python
429
# AD-specific password change
430
server = ldap3.Server('ldap://dc.company.com', use_ssl=True)
431
conn = ldap3.Connection(server, 'DOMAIN\\admin', 'adminpass', auto_bind=True)
432
433
# Admin changing user password
434
result = conn.extend.microsoft.modify_password(
435
user='cn=John Smith,ou=users,dc=company,dc=com',
436
new_password='NewComplexP@ssw0rd'
437
)
438
439
if result:
440
print("Password changed successfully")
441
else:
442
print(f"Password change failed: {conn.result}")
443
444
# User self-change (requires old password)
445
user_conn = ldap3.Connection(server, 'DOMAIN\\john.smith', 'oldpassword', auto_bind=True)
446
result = user_conn.extend.microsoft.modify_password(
447
user='cn=John Smith,ou=users,dc=company,dc=com',
448
new_password='NewComplexP@ssw0rd',
449
old_password='oldpassword'
450
)
451
```
452
453
### Novell eDirectory Operations
454
455
```python
456
# eDirectory specific operations
457
server = ldap3.Server('ldap://edir.company.com')
458
conn = ldap3.Connection(server, 'cn=admin,o=company', 'password', auto_bind=True)
459
460
# Get current bind DN
461
bind_dn = conn.extend.novell.get_bind_dn()
462
print(f"Currently bound as: {bind_dn}")
463
464
# Universal password operations
465
user_dn = 'cn=john,ou=people,o=company'
466
467
# Set universal password
468
result = conn.extend.novell.set_universal_password(user_dn, 'newpassword123')
469
if result:
470
print("Universal password set successfully")
471
472
# Get universal password (requires special privileges)
473
try:
474
password = conn.extend.novell.get_universal_password(user_dn)
475
print(f"Universal password retrieved: {password}")
476
except ldap3.LDAPExtensionError as e:
477
print(f"Cannot retrieve password: {e}")
478
```
479
480
### eDirectory Group Management
481
482
```python
483
# Efficient group membership operations
484
members = [
485
'cn=john,ou=people,o=company',
486
'cn=jane,ou=people,o=company',
487
'cn=bob,ou=people,o=company'
488
]
489
490
groups = [
491
'cn=developers,ou=groups,o=company',
492
'cn=employees,ou=groups,o=company'
493
]
494
495
# Add members to multiple groups atomically
496
result = conn.extend.novell.add_members_to_groups(
497
members=members,
498
groups=groups,
499
fix=True, # Fix any inconsistencies
500
transaction=True # Use transaction for atomicity
501
)
502
503
if result:
504
print("Members added to groups successfully")
505
506
# Check membership status
507
membership_info = conn.extend.novell.check_groups_memberships(
508
members=members,
509
groups=groups,
510
fix=False # Only check, don't fix
511
)
512
513
print("Membership status:")
514
for member, group_info in membership_info.items():
515
print(f" {member}: {group_info}")
516
```
517
518
### eDirectory Transaction Management
519
520
```python
521
# Atomic operations using transactions
522
try:
523
# Start transaction
524
transaction_id = conn.extend.novell.start_transaction()
525
print(f"Transaction started: {transaction_id}")
526
527
# Perform multiple operations
528
conn.add('cn=newuser1,ou=people,o=company',
529
object_class=['inetOrgPerson'],
530
attributes={'cn': 'New User 1', 'sn': 'User1'})
531
532
conn.add('cn=newuser2,ou=people,o=company',
533
object_class=['inetOrgPerson'],
534
attributes={'cn': 'New User 2', 'sn': 'User2'})
535
536
# Add users to group
537
conn.extend.novell.add_members_to_groups(
538
members=['cn=newuser1,ou=people,o=company', 'cn=newuser2,ou=people,o=company'],
539
groups=['cn=newusers,ou=groups,o=company'],
540
transaction=False # Already in transaction
541
)
542
543
# Commit transaction
544
result = conn.extend.novell.end_transaction(commit=True)
545
if result:
546
print("Transaction committed successfully")
547
else:
548
print("Transaction commit failed")
549
550
except Exception as e:
551
print(f"Error during transaction: {e}")
552
# Abort transaction
553
conn.extend.novell.end_transaction(commit=False)
554
print("Transaction aborted")
555
```
556
557
### eDirectory Replica Management
558
559
```python
560
# Server and partition information
561
server_dn = 'cn=server1,ou=servers,o=company'
562
563
# List all replicas on server
564
replicas = conn.extend.novell.list_replicas(server_dn)
565
print("Replicas on server:")
566
for replica in replicas:
567
print(f" Partition: {replica['partition_dn']}")
568
print(f" Type: {replica['replica_type']}")
569
print(f" State: {replica['replica_state']}")
570
571
# Get partition entry count
572
partition_dn = 'ou=people,o=company'
573
entry_count = conn.extend.novell.partition_entry_count(partition_dn)
574
print(f"Entries in {partition_dn}: {entry_count}")
575
576
# Get detailed replica information
577
replica_info = conn.extend.novell.replica_info(server_dn, partition_dn)
578
print(f"Replica info for {partition_dn}:")
579
print(f" Replica ID: {replica_info['replica_id']}")
580
print(f" Replica number: {replica_info['replica_number']}")
581
print(f" Sync status: {replica_info['sync_status']}")
582
```
583
584
### Error Handling for Extended Operations
585
586
```python
587
try:
588
# Attempt extended operation
589
result = conn.extend.standard.who_am_i()
590
print(f"WHO AM I result: {result}")
591
592
except ldap3.LDAPExtensionError as e:
593
print(f"Extended operation not supported: {e}")
594
595
except ldap3.LDAPOperationResult as e:
596
print(f"Operation failed with result: {e.result}")
597
598
except ldap3.LDAPCommunicationError as e:
599
print(f"Communication error: {e}")
600
601
# Check if specific extended operation is supported
602
if hasattr(conn.extend, 'microsoft'):
603
# Microsoft AD extensions are available
604
try:
605
sync_result = conn.extend.microsoft.dir_sync(
606
sync_base='dc=company,dc=com',
607
sync_filter='(objectClass=user)'
608
)
609
except ldap3.LDAPExtensionError:
610
print("DirSync not supported or insufficient permissions")
611
else:
612
print("Microsoft AD extensions not available")
613
```