Extended Operations¶
LDAP extended operations allow clients to perform actions beyond the standard CRUD model. These are protocol extensions, defined by unique OIDs, that enable specialized capabilities such as password modification, Start TLS, cancellation or custom administrative tasks.
Unlike controls, which modify existing operations, extended operations are stand-alone requests with their own semantics and result formats.
This section includes examples of:
Invoking extended operations e.g. by OID
Implementing custom extended operations
Extended operations provide a flexible mechanism for implementing advanced directory features while remaining compatible with the core LDAP protocol.
Who Am I?¶
The Who Am I? extended operation is built into most clients and available directly as a method of the connection.
dn = await conn.whoami()
print(dn)
Automatic object removal (refresh Time to Live (TTL))¶
Dynamic LDAP entries are automatically removed when their configured time-to-live (TTL) expires. To prevent expiration, these entries must be periodically refreshed. The refresh extended operation resets the TTL (specified in seconds), effectively extending the entry’s lifetime until the next refresh.
attrs = {
'objectClass': [b'inetOrgPerson', b'dynamicObject'],
'uid': [b'dynamic'],
'cn': [b'common name'],
'sn': [b'sir name'],
}
result = await conn.add(f'uid=dynamic,{base_dn}', attrs)
result = await conn.refresh_ttl(result.dn, 20)
print(result.extended_value) # 20
Transactions¶
Some directory servers support transactional updates. Transactions allow multiple modifications to be applied atomically. Extended operations are used to start, commit, or abort transactions.
from freeiam.ldap.controls import Controls, transaction
from freeiam.ldap.extended_operations import (
transaction_commit,
transaction_start,
)
result = await conn.extended(transaction_start(), transaction_start.response)
txn_id = result.extended_value
controls = Controls.set_server(None, transaction(txn_id, criticality=True))
try:
for i in range(10):
attrs = {
'objectClass': [b'inetOrgPerson'],
'uid': [f'txn{i}'.encode()],
'cn': [b'common name'],
'sn': [b'sir name'],
}
await conn.add(f'uid=txn{i},{base_dn}', attrs, controls=controls)
except Exception: # noqa: BLE001
# abort transaction
await conn.extended(
transaction_commit(txn_id, commit=False),
transaction_commit.response,
)
else:
await conn.extended(
transaction_commit(txn_id, commit=True), transaction_commit.response
)
This can also be achieved with a context manager:
async with conn.transaction() as transaction_id:
print(transaction_id) # None for OpenLDAP
# it sets the connections wide transaction control with the transaction ID
# so that it gets applied automatically for all subsequent requests.
# If any uncatched exception happens here, the transaction is aborted!
# otherwise it's commited.
for i in range(10):
attrs = {
'objectClass': [b'inetOrgPerson'],
'uid': [f'txn{i}'.encode()],
'cn': [b'common name'],
'sn': [b'sir name'],
}
await conn.add(f'uid=txn{i},{base_dn}', attrs)
Sync Replication¶
Certain replication mechanisms, such as SyncRepl, rely on extended
operations to maintain replication state or retrieve changes. Clients
can use these operations to participate in replication or query
replication metadata.
# TODO: implement syncrepl examples
Perform extended operations by OID¶
Extended operations are always identified by a unique OID. They may be standard, vendor-specific, or custom, and can be invoked using existing request and response implementations or by providing custom implementations when needed.
from freeiam.ldap.extended_operation import ExtendedRequest, ExtendedResponse
# custom extended operations can be implemented by yourself
# by creating a suitable subclasses of the above
class FooExtended(ExtendedRequest):
requestName = '1.3.6.1.1.21.1' # insert OID HERE
# implement "encodedRequestValue" method, which
# returns the BER-encoded ASN.1 request value
class FooExtendedResponse(ExtendedResponse):
responseName = '1.3.6.1.1.21.3' # insert OID here
# implement "decodeResponseValue" method, which
# decodes the BER-encoded ASN.1 extended operation response value
# you could even build modules for OpenLDAP implementing
# custom extended operations
await conn.extended(FooExtended(), FooExtendedResponse)