or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

abstract.mdconfig.mdconnection.mdextended.mdindex.mdoperations.md

extended.mddocs/

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

```