or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

active-directory.mdasync-frameworks.mdconnection-pooling.mdcore-ldap.mdindex.mdldif-support.mdutilities-errors.md

core-ldap.mddocs/

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

```