or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

abstract.mddocs/

0

# Abstract Layer

1

2

High-level ORM-like interface with object definitions, attribute definitions, entry objects, and reader classes for simplified LDAP programming.

3

4

## Capabilities

5

6

### Object Definition

7

8

Define LDAP object schemas with attribute definitions for type-safe and validated entry manipulation.

9

10

```python { .api }

11

class ObjectDef:

12

def __init__(self, object_class=None):

13

"""

14

Define LDAP object schema.

15

16

Args:

17

object_class (str or list, optional): LDAP object class name(s)

18

"""

19

20

def add(self, definition):

21

"""

22

Add attribute definition to object.

23

24

Args:

25

definition (AttrDef): Attribute definition object

26

"""

27

28

def remove(self, item):

29

"""

30

Remove attribute definition.

31

32

Args:

33

item (str or AttrDef): Attribute name or definition to remove

34

"""

35

36

def clear(self):

37

"""Clear all attribute definitions."""

38

39

def __iadd__(self, other):

40

"""Add attribute definition using += operator."""

41

42

def __isub__(self, other):

43

"""Remove attribute definition using -= operator."""

44

45

def __contains__(self, item):

46

"""Check if attribute definition exists."""

47

48

def __getitem__(self, item):

49

"""Get attribute definition by name."""

50

51

def __len__(self):

52

"""Get number of attribute definitions."""

53

54

def __iter__(self):

55

"""Iterate over attribute definitions."""

56

```

57

58

### Attribute Definition

59

60

Define individual LDAP attributes with validation, transformation, and dereferencing rules.

61

62

```python { .api }

63

class AttrDef:

64

def __init__(self, name, key=None, validate=None, pre_query=None, post_query=None,

65

default=NotImplemented, dereference_dn=None, description=None):

66

"""

67

Define LDAP attribute with validation and transformation.

68

69

Args:

70

name (str): LDAP attribute name

71

key (str, optional): Friendly name for Python access

72

validate (callable, optional): Validation function for values

73

pre_query (callable, optional): Transform values before LDAP query

74

post_query (callable, optional): Transform values after LDAP response

75

default: Default value if attribute is missing

76

dereference_dn (str, optional): DN dereferencing mode

77

description (str, optional): Attribute description

78

"""

79

```

80

81

**Properties**:

82

- `name`: LDAP attribute name

83

- `key`: Python-friendly name

84

- `validate`: Validation function

85

- `pre_query`: Pre-query transformation

86

- `post_query`: Post-query transformation

87

- `default`: Default value

88

- `dereference_dn`: DN dereferencing mode

89

- `description`: Human-readable description

90

91

### Attribute Value Container

92

93

Container for LDAP attribute values with automatic type conversion and validation.

94

95

```python { .api }

96

class Attribute:

97

def __init__(self, attr_def, entry):

98

"""

99

Create attribute value container.

100

101

Args:

102

attr_def (AttrDef): Attribute definition

103

entry (Entry): Parent entry object

104

"""

105

106

@property

107

def value(self):

108

"""

109

Get attribute value(s).

110

111

Returns:

112

Single value for single-valued attributes, list for multi-valued

113

"""

114

115

def __len__(self):

116

"""Get number of values."""

117

118

def __iter__(self):

119

"""Iterate over values."""

120

121

def __getitem__(self, item):

122

"""Get value by index."""

123

124

def __eq__(self, other):

125

"""Compare attribute values."""

126

```

127

128

### Entry Object

129

130

High-level representation of LDAP entries with attribute access and manipulation methods.

131

132

```python { .api }

133

class Entry:

134

def __init__(self, dn, reader):

135

"""

136

Create entry object.

137

138

Args:

139

dn (str): Distinguished Name of entry

140

reader (Reader): Reader object that retrieved this entry

141

"""

142

143

def entry_get_dn(self):

144

"""

145

Get entry distinguished name.

146

147

Returns:

148

str: Entry DN

149

"""

150

151

def entry_get_response(self):

152

"""

153

Get raw LDAP response for entry.

154

155

Returns:

156

dict: Raw LDAP response

157

"""

158

159

def entry_get_reader(self):

160

"""

161

Get reader object that retrieved this entry.

162

163

Returns:

164

Reader: Reader object

165

"""

166

167

def entry_get_raw_attributes(self):

168

"""

169

Get raw attribute values from LDAP response.

170

171

Returns:

172

dict: Raw attributes

173

"""

174

175

def entry_get_raw_attribute(self, name):

176

"""

177

Get raw values for specific attribute.

178

179

Args:

180

name (str): Attribute name

181

182

Returns:

183

list: Raw attribute values

184

"""

185

186

def entry_get_attribute_names(self):

187

"""

188

Get list of available attribute names.

189

190

Returns:

191

list: Attribute names

192

"""

193

194

def entry_get_attributes_dict(self):

195

"""

196

Get attributes as dictionary.

197

198

Returns:

199

dict: Attribute name to value mapping

200

"""

201

202

def entry_refresh_from_reader(self):

203

"""Refresh entry data from reader."""

204

205

def entry_to_json(self):

206

"""

207

Convert entry to JSON format.

208

209

Returns:

210

str: Entry in JSON format

211

"""

212

213

def entry_to_ldif(self):

214

"""

215

Convert entry to LDIF format.

216

217

Returns:

218

str: Entry in LDIF format

219

"""

220

221

def __iter__(self):

222

"""Iterate over attribute names."""

223

224

def __contains__(self, item):

225

"""Check if attribute exists in entry."""

226

227

def __getitem__(self, item):

228

"""Access attribute by name."""

229

230

def __eq__(self, other):

231

"""Compare entries."""

232

233

def __lt__(self, other):

234

"""Compare entries for sorting."""

235

236

def entry_get_changes_dict(self):

237

"""

238

Get dictionary of pending changes for entry.

239

240

Returns:

241

dict: Dictionary of changes by attribute name

242

"""

243

244

def entry_commit_changes(self):

245

"""

246

Commit pending changes to LDAP server.

247

248

Returns:

249

bool: True if changes committed successfully

250

"""

251

252

def entry_discard_changes(self):

253

"""Discard pending changes without committing."""

254

255

def entry_delete(self):

256

"""

257

Delete entry from LDAP server.

258

259

Returns:

260

bool: True if entry deleted successfully

261

"""

262

263

def entry_refresh(self):

264

"""

265

Refresh entry data from LDAP server.

266

267

Returns:

268

bool: True if entry refreshed successfully

269

"""

270

271

def entry_move(self, destination_dn):

272

"""

273

Move entry to new DN location.

274

275

Args:

276

destination_dn (str): Destination DN for entry

277

278

Returns:

279

bool: True if entry moved successfully

280

"""

281

282

def entry_rename(self, new_name):

283

"""

284

Rename entry with new relative DN.

285

286

Args:

287

new_name (str): New relative DN

288

289

Returns:

290

bool: True if entry renamed successfully

291

"""

292

```

293

294

**Dynamic Attribute Access**: Attributes can be accessed as properties:

295

```python

296

entry.cn # Access 'cn' attribute

297

entry.mail # Access 'mail' attribute

298

entry.memberOf # Access 'memberOf' attribute

299

```

300

301

### Reader Class

302

303

High-level search interface with object-oriented query building and result processing.

304

305

```python { .api }

306

class Reader:

307

def __init__(self, connection, object_def, query, base, components_in_and=True,

308

sub_tree=True, get_operational_attributes=False, controls=None):

309

"""

310

Create reader for high-level LDAP searches.

311

312

Args:

313

connection (Connection): LDAP connection object

314

object_def (ObjectDef): Object definition for entries

315

query (str): Search query string

316

base (str): Search base DN

317

components_in_and (bool): Combine query components with AND logic

318

sub_tree (bool): Use subtree search scope

319

get_operational_attributes (bool): Include operational attributes

320

controls (list, optional): LDAP controls for search

321

"""

322

323

def search(self):

324

"""

325

Execute search and populate entries.

326

327

Returns:

328

bool: True if search successful

329

"""

330

331

def search_level(self):

332

"""

333

Execute single-level search.

334

335

Returns:

336

bool: True if search successful

337

"""

338

339

def search_subtree(self):

340

"""

341

Execute subtree search.

342

343

Returns:

344

bool: True if search successful

345

"""

346

347

def search_object(self, entry_dn):

348

"""

349

Search for specific entry by DN.

350

351

Args:

352

entry_dn (str): Distinguished Name to search for

353

354

Returns:

355

bool: True if search successful

356

"""

357

358

def search_size_limit(self, size_limit):

359

"""

360

Execute search with size limit.

361

362

Args:

363

size_limit (int): Maximum entries to return

364

365

Returns:

366

bool: True if search successful

367

"""

368

369

def search_time_limit(self, time_limit):

370

"""

371

Execute search with time limit.

372

373

Args:

374

time_limit (int): Search time limit in seconds

375

376

Returns:

377

bool: True if search successful

378

"""

379

380

def search_types_only(self):

381

"""

382

Execute search returning attribute types only.

383

384

Returns:

385

bool: True if search successful

386

"""

387

388

def search_paged(self, paged_size, paged_criticality=False):

389

"""

390

Execute paged search.

391

392

Args:

393

paged_size (int): Page size

394

paged_criticality (bool): Paged search criticality

395

396

Returns:

397

bool: True if search successful

398

"""

399

400

def clear(self):

401

"""Clear search results."""

402

403

def reset(self):

404

"""Reset reader state."""

405

406

def __iter__(self):

407

"""Iterate over found entries."""

408

409

def __getitem__(self, item):

410

"""Get entry by index."""

411

412

def __len__(self):

413

"""Get number of entries found."""

414

```

415

416

**Properties**:

417

- `definition`: Object definition used

418

- `query`: Current search query

419

- `components_in_and`: Query component logic

420

- `entries`: List of found Entry objects

421

422

### Operational Attribute

423

424

Specialized attribute container for LDAP operational attributes.

425

426

```python { .api }

427

class OperationalAttribute(Attribute):

428

"""

429

Operational attribute container (inherits from Attribute).

430

431

Handles LDAP operational attributes like createTimestamp,

432

modifyTimestamp, entryUUID, etc.

433

"""

434

```

435

436

## Usage Examples

437

438

### Object Definition and Attribute Definition

439

440

```python

441

import ldap3

442

443

# Define object schema

444

person = ldap3.ObjectDef('inetOrgPerson')

445

446

# Add attribute definitions with validation

447

person += ldap3.AttrDef('cn', key='name')

448

person += ldap3.AttrDef('mail', key='email', validate=lambda x: '@' in x)

449

person += ldap3.AttrDef('telephoneNumber', key='phone')

450

person += ldap3.AttrDef('employeeNumber', key='emp_id',

451

validate=lambda x: x.isdigit())

452

453

# Add attribute with default value

454

person += ldap3.AttrDef('department', default='IT')

455

```

456

457

### Reader-based Searching

458

459

```python

460

# Create connection and reader

461

server = ldap3.Server('ldap://ldap.example.com')

462

conn = ldap3.Connection(server, 'cn=user,dc=example,dc=com', 'password', auto_bind=True)

463

464

# Create reader with object definition

465

reader = ldap3.Reader(conn, person, 'name: John*', 'ou=people,dc=example,dc=com')

466

467

# Perform search

468

reader.search()

469

470

# Access results through entry objects

471

for entry in reader:

472

print(f"Name: {entry.name}") # Using key from AttrDef

473

print(f"Email: {entry.email}") # Validated email attribute

474

print(f"Phone: {entry.phone}")

475

print(f"Employee ID: {entry.emp_id}") # Validated numeric

476

print(f"Department: {entry.department}") # May use default value

477

print(f"DN: {entry.entry_get_dn()}")

478

print("---")

479

```

480

481

### Advanced Query Building

482

483

```python

484

# Complex queries using Reader

485

reader = ldap3.Reader(conn, person,

486

'name: John* & email: *@company.com | department: Engineering',

487

'dc=example,dc=com')

488

489

# Execute subtree search

490

reader.search_subtree()

491

492

# Access entry details

493

for entry in reader:

494

# Get all attributes as dictionary

495

attrs = entry.entry_get_attributes_dict()

496

print(f"All attributes: {attrs}")

497

498

# Convert to different formats

499

json_data = entry.entry_to_json()

500

ldif_data = entry.entry_to_ldif()

501

```

502

503

### Working with Multi-valued Attributes

504

505

```python

506

# Define group object with member attribute

507

group = ldap3.ObjectDef('groupOfNames')

508

group += ldap3.AttrDef('cn', key='name')

509

group += ldap3.AttrDef('member', key='members')

510

511

reader = ldap3.Reader(conn, group, 'name: *Admin*', 'ou=groups,dc=example,dc=com')

512

reader.search()

513

514

for group_entry in reader:

515

print(f"Group: {group_entry.name}")

516

517

# Multi-valued attribute access

518

if hasattr(group_entry, 'members'):

519

print(f"Number of members: {len(group_entry.members)}")

520

for member in group_entry.members:

521

print(f" Member: {member}")

522

```

523

524

### Custom Validation and Transformation

525

526

```python

527

import re

528

from datetime import datetime

529

530

def validate_email(email):

531

"""Email validation function."""

532

pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

533

return re.match(pattern, email) is not None

534

535

def format_phone(phone):

536

"""Phone number formatting function."""

537

# Remove non-digits

538

digits = ''.join(filter(str.isdigit, phone))

539

if len(digits) == 10:

540

return f"({digits[:3]}) {digits[3:6]}-{digits[6:]}"

541

return phone

542

543

def parse_timestamp(timestamp):

544

"""Parse LDAP timestamp to datetime object."""

545

if isinstance(timestamp, list):

546

timestamp = timestamp[0]

547

return datetime.strptime(timestamp, '%Y%m%d%H%M%SZ')

548

549

# Create object definition with custom functions

550

person = ldap3.ObjectDef('inetOrgPerson')

551

person += ldap3.AttrDef('cn', key='name')

552

person += ldap3.AttrDef('mail', key='email', validate=validate_email)

553

person += ldap3.AttrDef('telephoneNumber', key='phone', post_query=format_phone)

554

person += ldap3.AttrDef('createTimestamp', key='created', post_query=parse_timestamp)

555

556

reader = ldap3.Reader(conn, person, 'name: *', 'ou=people,dc=example,dc=com',

557

get_operational_attributes=True)

558

reader.search()

559

560

for entry in reader:

561

print(f"Name: {entry.name}")

562

print(f"Email: {entry.email}") # Validated

563

print(f"Phone: {entry.phone}") # Formatted

564

print(f"Created: {entry.created}") # Parsed datetime

565

```

566

567

### Pagination with Reader

568

569

```python

570

# Reader-based paged search

571

reader = ldap3.Reader(conn, person, 'name: *', 'dc=example,dc=com')

572

573

# Search with pagination

574

page_size = 100

575

reader.search_paged(page_size)

576

577

print(f"Found {len(reader)} entries in first page")

578

579

# Continue with more pages if needed

580

while True:

581

# Check if more results available

582

if not conn.result.get('controls', {}).get('1.2.840.113556.1.4.319', {}).get('value', {}).get('cookie'):

583

break

584

585

reader.search_paged(page_size)

586

print(f"Found {len(reader)} additional entries")

587

```

588

589

### DN Dereferencing

590

591

```python

592

# Dereference DN attributes to get referenced entry data

593

person = ldap3.ObjectDef('inetOrgPerson')

594

person += ldap3.AttrDef('cn', key='name')

595

person += ldap3.AttrDef('manager', key='manager_info', dereference_dn='cn')

596

597

reader = ldap3.Reader(conn, person, 'name: *', 'ou=people,dc=example,dc=com')

598

reader.search()

599

600

for entry in reader:

601

print(f"Employee: {entry.name}")

602

if hasattr(entry, 'manager_info'):

603

print(f"Manager: {entry.manager_info}") # Dereferenced manager CN

604

```

605

606

### Error Handling in Abstract Layer

607

608

```python

609

try:

610

# Create object definition

611

person = ldap3.ObjectDef('inetOrgPerson')

612

person += ldap3.AttrDef('mail', validate=lambda x: '@' in x)

613

614

reader = ldap3.Reader(conn, person, 'mail: *', 'dc=example,dc=com')

615

reader.search()

616

617

for entry in reader:

618

# Access validated attributes

619

print(f"Valid email: {entry.mail}")

620

621

except ldap3.LDAPAttributeError as e:

622

print(f"Attribute error: {e}")

623

except ldap3.LDAPEntryError as e:

624

print(f"Entry error: {e}")

625

except ldap3.LDAPReaderError as e:

626

print(f"Reader error: {e}")

627

```

628

629

### Combining Abstract Layer with Raw Operations

630

631

```python

632

# Use abstract layer for searching, raw operations for modifications

633

reader = ldap3.Reader(conn, person, 'name: John*', 'ou=people,dc=example,dc=com')

634

reader.search()

635

636

for entry in reader:

637

# Get DN from abstract entry

638

entry_dn = entry.entry_get_dn()

639

640

# Use raw connection for modifications

641

changes = {'description': [(ldap3.MODIFY_REPLACE, f'Updated by script on {datetime.now()}')]}

642

conn.modify(entry_dn, changes)

643

644

print(f"Updated entry: {entry.name}")

645

```