anyldap.ldap package¶
anyldap.ldap is python-ldap’s ldap module with every operation
awaited. The names, the arguments they take and the values they hand back
are the ones python-ldap documents, so code ported from it reads the same
with await in front:
from anyldap import ldap
async def example() -> None:
async with ldap.initialize("ldap://ldap.example.com") as connection:
await connection.simple_bind_s("cn=admin,dc=example,dc=com", "secret")
results = await connection.search_s(
"dc=example,dc=com", ldap.SCOPE_SUBTREE, "(cn=Babs*)"
)
for dn, attributes in results:
print(dn, attributes)
initialize() opens nothing: the first operation that is awaited connects,
which is what lets it stay the plain call it is in python-ldap. The
connection is closed by unbind_s(), and async with closes it however
the block ends.
Nothing runs in the background. As in python-ldap, an operation is sent when
it is started and the connection is read while a result is being waited for,
so a connection needs no task group and belongs to whichever task holds it.
Operations can be started with search_ext(), add() and the rest, and
collected later by message id with result3(); result(msgid,
all=ldap.MSG_ONE) walks a large search one entry at a time.
Binding with SASL¶
sasl_interactive_bind_s() drives the exchange, asking the mechanism what
to send for as long as the server says the bind is still in progress:
from anyldap import ldap
async with ldap.initialize("ldapi:///run/slapd/ldapi") as connection:
await connection.sasl_external_bind_s()
print(await connection.whoami_s())
The mechanisms in anyldap.ldap.sasl answer for themselves rather than
through Cyrus SASL: external, plain, cram_md5, digest_md5,
and gssapi if the gssapi package is installed, which is not a
dependency. A mechanism of your own works anywhere these do, as long as it
has a mech and a process() that answers a challenge.
Controls¶
Controls are the objects anyldap.ldap.controls builds, encoded with
the BER library anyldap already has rather than with pyasn1, and a response
hands back the ones it carried, read into the classes that know them:
from anyldap.ldap.controls import SimplePagedResultsControl
paged = SimplePagedResultsControl(True, size=100, cookie=b"")
while True:
msgid = await connection.search_ext(base, scope, serverctrls=[paged])
rtype, data, rmsgid, answered = await connection.result3(msgid)
...
cookies = [
control.cookie
for control in answered
if control.controlType == ldap.CONTROL_PAGEDRESULTS
]
if not cookies or not cookies[0]:
break
paged.cookie = cookies[0]
The controls with a class of their own are paged results, pre-read and
post-read, server-side sorting, the password policy response, OpenLDAP’s
no-op search, assertion and matched-values, ManageDSAIT, relax rules,
proxied authorization and the authorization identity pair; each lives in
the module python-ldap keeps it in, and the names are re-exported from
anyldap.ldap.controls as python-ldap re-exports them. Anything else
is an LDAPControl carrying
the bytes as they came, and a control of your own only has to encode and
decode its own value. A (type, criticality, value) triple is still
accepted wherever controls are.
Schema¶
anyldap.ldap.schema reads what a server publishes about itself into
the model classes python-ldap names, on top of the schema parsing in
anyldap.schema:
subschema = await connection.read_schema_s()
person = subschema.get_obj(ldap.schema.ObjectClass, "person")
must, may = subschema.attribute_types(["inetOrgPerson"])
Every kind of definition is read: object classes, attribute types, matching
rules and their uses, syntaxes, content rules, structure rules and name
forms. A definition writes itself back out again – str(person) is the
objectClasses value the server published – and x_origin and the
rest of the X- fields a definition carries are read into extensions.
Entry is an entry that knows the schema
its attributes are described by, so entry["cn"], entry["commonName"]
and entry["2.5.4.3"] are the same attribute.
urlfetch() is python-ldap’s, taking an LDAP URL and opening a connection
of its own to ask; read_schema_s() is not a method python-ldap has, and
uses the connection that is already open:
dn, subschema = await ldap.schema.urlfetch("ldap://localhost")
It takes the address of an LDIF file as well, and reads the schema out of its first record – which is how a schema someone else published is read without a server to ask:
dn, subschema = await ldap.schema.urlfetch("file:///tmp/subschema.ldif")
Only file:, http: and https: are read, and anything else is
refused. python-ldap hands the address to urllib.request.urlopen(),
which fetches whatever scheme it happens to support – so an address that
was meant to name a file can turn out to be a request, and one that was
meant to be a request can turn out to read a file. An http: address is
fetched with httpx2. The same two
rules apply to the URLs anyldap.ldap.ldif fetches when it is told to
fetch any.
A definition may name itself rather than be numbered
(( nsEncryptionConfig-oid NAME 'nsEncryptionConfig' ... )), which RFC
4512 section 1.4 allows and 389-ds publishes a great many of.
TLS¶
The OPT_X_TLS_* options build the ssl.SSLContext a connection
is raised with – the certificates to trust, the certificate to send, what
to check, which protocol versions and ciphers – and OPT_X_TLS_NEWCTX
starts a new one. A context can be passed to initialize() instead, and
then it is used as it stands. ldaps:// raises TLS before anything is
sent; start_tls_s() raises it on a connection that is already open.
URLs¶
anyldap.ldap.ldapurl is python-ldap’s top-level ldapurl module:
an LDAP URL taken apart into what it says, and written back out again:
from anyldap.ldap.ldapurl import LDAPUrl
url = LDAPUrl("ldap://localhost/dc=example,dc=com?cn?sub?(cn=jack)")
async with ldap.initialize(url.initializeUrl()) as connection:
found = await connection.search_s(url.dn, url.scope, url.filterstr)
who and cred are the bind DN and its password, which a URL carries
as the bindname and X-BINDPW extensions.
LDIF¶
anyldap.ldap.ldif is python-ldap’s top-level ldif module. anyldap
has an LDIF reader of its own in anyldap.protocols.ldap.ldifprotocol,
but it is a line-receiving protocol answering with the objects the rest of
anyldap is built out of, which is not what code written against python-ldap
asks for. Nothing in it touches the network, so nothing in it is awaited:
from anyldap.ldap import ldif
records = ldif.LDIFRecordList(open("people.ldif"))
records.parse()
for dn, entry in records.all_records:
await connection.add_s(dn, ldap.modlist.addModlist(entry))
LDIFWriter writes entry records and change records, folding long lines
and base64-encoding whatever RFC 2849 does not allow to be written as it
stands; LDIFParser reads them, and a class of your own overrides
handle() or handle_modify(). LDIFRecordList collects them all,
and LDIFCopy reads and writes at once. CreateLDIF() and
ParseLDIF(), which python-ldap has deprecated, are not here.
A value an LDIF names by URL (jpegPhoto:< file:///…) is fetched only
when process_url_schemes says which schemes to fetch, and is left unread
otherwise – which is what python-ldap does too. Of the schemes that can be
asked for, file, http and https are the ones that are read.
Following referrals¶
A server that does not hold what was asked for can say where to look
instead, and unless OPT_REFERRALS is turned off the operation is made
again there, which is what libldap does and so what python-ldap inherits:
# On by default, and read back as -1 however it was set, as libldap
# keeps it.
connection.set_option(ldap.OPT_REFERRALS, 0)
A referral is followed anonymously: it says where to look and nothing about
whose credentials may be sent there. For the same reason a bind is never
followed – the referral it is answered with is raised instead. A search
continuation is followed too, and what the other server holds is added to
what the search found; the continuation itself stays among the results as
(None, [uri, ...]), which is what python-ldap hands back whether or not
it followed one.
When no server a referral names can be reached, the referral is raised as
REFERRAL, carrying the URLs as its info and
how far the name was recognised as its matched. Referrals are followed
five deep, after which one is called a loop and
REFERRAL_LIMIT_EXCEEDED is raised. That is
libldap’s own limit; like libldap, this does not let it be set, so asking
for OPT_REFHOPLIMIT is an error.
Reading a long search¶
anyldap.ldap.asyncsearch reads a search result by result rather than
asking for all of it at once, which is what python-ldap’s asyncsearch
does:
from anyldap.ldap import asyncsearch
found = asyncsearch.Dict(connection)
await found.startSearch("dc=example,dc=com", ldap.SCOPE_SUBTREE, "(cn=*)")
await found.processResults()
print(found.allEntries)
List, Dict, IndexedDict, FileWriter and LDIFWriter are
python-ldap’s, and a handler of your own overrides
_processSingleResult(). Only startSearch() and processResults()
touch the connection and are awaited; the hooks are plain methods, because
they are called while a result is being dispatched.
Reconnecting¶
ReconnectLDAPObject tries an operation
again on a connection that went away, opening it and putting it back as it
was: the options that were set, StartTLS if it had been raised, and the last
bind that was made:
connection = ldap.ReconnectLDAPObject(uri, retry_max=3, retry_delay=5.0)
await connection.simple_bind_s(who, cred)
# Whatever happens to the server in between, this is answered.
entries = await connection.search_ext_s(base, ldap.SCOPE_SUBTREE)
Unlike python-ldap’s, it does not open the connection before the first operation: nothing is sent until something is awaited, so there is nothing to reconnect until an operation has failed.
Keeping a copy¶
anyldap.ldap.syncrepl is RFC 4533, which is how a copy of what a
server holds is kept up to date. A connection mixes in
SyncreplConsumer, overrides the
syncrepl_* methods to keep whatever it is keeping, and polls:
class Consumer(ldap.syncrepl.SyncreplConsumer, ldap.SimpleLDAPObject):
def syncrepl_entry(self, dn, attrs, uuid):
self.entries[uuid] = (dn, attrs)
msgid = await connection.syncrepl_search(base, ldap.SCOPE_SUBTREE)
while await connection.syncrepl_poll(msgid=msgid):
pass
refreshOnly stops once the copy has caught up and refreshAndPersist
stays open and keeps being told. cancel_s() stops a search that is
staying open, and unlike abandon() the server answers to say it has.
Extended operations¶
anyldap.ldap.extop is what an extended operation of your own is
built out of: a request that knows its OID and writes its value, and a
response that reads one:
from anyldap.ldap.extop.dds import RefreshRequest, RefreshResponse
request = RefreshRequest(entryName=dn, requestTtl=3600)
name, value = await connection.extop_s(
pureldap.LDAPExtendedRequest(
requestName=request.requestName,
requestValue=request.encodedRequestValue(),
)
)
left = RefreshResponse(name, value).responseTtl
extop.dds is RFC 2589, entries that go away by themselves, and
extop.passwd reads the password a server made up when it was asked to
change one without being given a new one.
What is not here¶
GSSAPI without the ``gssapi`` package: the mechanism is
anyldap.ldap.sasl.gssapi, and it says so if it is asked for without Kerberos to hand. The SASL security layer it negotiates is always none: what protects a connection here is TLS.The ``OPT_X_SASL_*`` options beyond what they say: they supply the defaults a mechanism reads and report what the bind ended up with, rather than configuring Cyrus SASL, which is not what does the exchange here.
A referral that points at itself stops rather than being followed until something else gives up. libldap keeps going, and what ends it there is the timeout rather than the hop limit.
abandon() sends a real abandon request and forgets the operation, but
the server may already have answered.
anyldap.ldap module¶
python-ldap’s ldap module, with every operation awaited.
The names, arguments and return values are the ones python-ldap documents:
from anyldap import ldap
async def example() -> None:
async with ldap.initialize("ldap://ldap.example.com") as connection:
await connection.simple_bind_s("cn=admin,dc=example,dc=com", "secret")
for dn, attributes in await connection.search_s(
"dc=example,dc=com", ldap.SCOPE_SUBTREE, "(cn=Babs*)"
):
print(dn, attributes)
ldap.controls, ldap.schema, ldap.sasl, ldap.dn,
ldap.filter, ldap.modlist and ldap.cidict are here too, and so
are the TLS options: setting OPT_X_TLS_CACERTFILE and its neighbours
builds the ssl.SSLContext the connection is raised with, and one can be
passed to initialize() instead.
SASL binds are made with the mechanisms in anyldap.ldap.sasl, which
answer for themselves rather than through Cyrus SASL: EXTERNAL, PLAIN,
CRAM-MD5, DIGEST-MD5, and GSSAPI if the gssapi package is installed. A
mechanism of your own works if it answers process() the same way.
ldap.asyncsearch reads a long search result by result,
ldap.syncrepl keeps a copy of what a server holds, and
ReconnectLDAPObject opens a connection again when the server goes away
and tries the operation once more.
- exception anyldap.ldap.ADMINLIMIT_EXCEEDED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Administrative limit exceeded'¶
- errnum: ClassVar[int | None] = 11¶
- anyldap.ldap.ADMIN_LIMIT_EXCEEDED¶
alias of
ADMINLIMIT_EXCEEDED
- exception anyldap.ldap.AFFECTS_MULTIPLE_DSAS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Operation affects multiple DSAs'¶
- errnum: ClassVar[int | None] = 71¶
- exception anyldap.ldap.ALIAS_DEREF_PROBLEM[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Alias dereferencing problem'¶
- errnum: ClassVar[int | None] = 36¶
- exception anyldap.ldap.ALIAS_PROBLEM[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Alias problem'¶
- errnum: ClassVar[int | None] = 33¶
- exception anyldap.ldap.ALREADY_EXISTS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Already exists'¶
- errnum: ClassVar[int | None] = 68¶
- exception anyldap.ldap.ASSERTION_FAILED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Assertion failed'¶
- errnum: ClassVar[int | None] = 122¶
- exception anyldap.ldap.AUTH_METHOD_NOT_SUPPORTED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Authentication method not supported'¶
- errnum: ClassVar[int | None] = 7¶
- exception anyldap.ldap.AUTH_UNKNOWN[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Unknown authentication method'¶
- errnum: ClassVar[int | None] = -6¶
- exception anyldap.ldap.BUSY[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Server is busy'¶
- errnum: ClassVar[int | None] = 51¶
- exception anyldap.ldap.CANCELLED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Cancelled'¶
- errnum: ClassVar[int | None] = 118¶
- exception anyldap.ldap.CANNOT_CANCEL[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Cannot cancel'¶
- errnum: ClassVar[int | None] = 121¶
- exception anyldap.ldap.CLIENT_LOOP[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Client loop'¶
- errnum: ClassVar[int | None] = -16¶
- exception anyldap.ldap.COMPARE_FALSE[source]¶
Bases:
LDAPErrorA compare that answered no; compare_s() turns this into False.
- desc: ClassVar[str] = 'Compare False'¶
- errnum: ClassVar[int | None] = 5¶
- exception anyldap.ldap.COMPARE_TRUE[source]¶
Bases:
LDAPErrorA compare that answered yes; compare_s() turns this into True.
- desc: ClassVar[str] = 'Compare True'¶
- errnum: ClassVar[int | None] = 6¶
- exception anyldap.ldap.CONFIDENTIALITY_REQUIRED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Confidentiality required'¶
- errnum: ClassVar[int | None] = 13¶
- exception anyldap.ldap.CONNECT_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Connect error'¶
- errnum: ClassVar[int | None] = -11¶
- exception anyldap.ldap.CONSTRAINT_VIOLATION[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Constraint violation'¶
- errnum: ClassVar[int | None] = 19¶
- exception anyldap.ldap.CONTROL_NOT_FOUND[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Control not found'¶
- errnum: ClassVar[int | None] = -13¶
- exception anyldap.ldap.DECODING_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Decoding error'¶
- errnum: ClassVar[int | None] = -4¶
- exception anyldap.ldap.ENCODING_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Encoding error'¶
- errnum: ClassVar[int | None] = -3¶
- exception anyldap.ldap.FILTER_ERROR[source]¶
Bases:
LDAPErrorThe search filter could not be parsed.
- desc: ClassVar[str] = 'Bad search filter'¶
- errnum: ClassVar[int | None] = -7¶
- exception anyldap.ldap.INAPPROPRIATE_AUTH[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Inappropriate authentication'¶
- errnum: ClassVar[int | None] = 48¶
- exception anyldap.ldap.INAPPROPRIATE_MATCHING[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Inappropriate matching'¶
- errnum: ClassVar[int | None] = 18¶
- exception anyldap.ldap.INSUFFICIENT_ACCESS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Insufficient access'¶
- errnum: ClassVar[int | None] = 50¶
- exception anyldap.ldap.INVALID_CREDENTIALS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Invalid credentials'¶
- errnum: ClassVar[int | None] = 49¶
- exception anyldap.ldap.INVALID_DN_SYNTAX[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Invalid DN syntax'¶
- errnum: ClassVar[int | None] = 34¶
- exception anyldap.ldap.INVALID_SYNTAX[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Invalid syntax'¶
- errnum: ClassVar[int | None] = 21¶
- exception anyldap.ldap.IS_LEAF[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Is a leaf'¶
- errnum: ClassVar[int | None] = 35¶
- exception anyldap.ldap.LDAPError[source]¶
Bases:
ExceptionThe base of every error raised here, as
ldap.LDAPErroris.- desc: ClassVar[str] = 'Unknown error'¶
- errnum: ClassVar[int | None] = None¶
- property info: str | None¶
The diagnostic message the server sent, if it sent one.
- anyldap.ldap.LDAPObject¶
alias of
SimpleLDAPObject
- exception anyldap.ldap.LOCAL_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Local error'¶
- errnum: ClassVar[int | None] = -2¶
- exception anyldap.ldap.LOOP_DETECT[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Loop detected'¶
- errnum: ClassVar[int | None] = 54¶
- exception anyldap.ldap.MORE_RESULTS_TO_RETURN[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'More results to return'¶
- errnum: ClassVar[int | None] = -15¶
- exception anyldap.ldap.NAMING_VIOLATION[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Naming violation'¶
- errnum: ClassVar[int | None] = 64¶
- exception anyldap.ldap.NOT_ALLOWED_ON_NONLEAF[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Operation not allowed on non-leaf'¶
- errnum: ClassVar[int | None] = 66¶
- exception anyldap.ldap.NOT_ALLOWED_ON_RDN[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Operation not allowed on RDN'¶
- errnum: ClassVar[int | None] = 67¶
- exception anyldap.ldap.NOT_SUPPORTED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Not supported'¶
- errnum: ClassVar[int | None] = -12¶
- exception anyldap.ldap.NO_MEMORY[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Out of memory'¶
- errnum: ClassVar[int | None] = -10¶
- exception anyldap.ldap.NO_OBJECT_CLASS_MODS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Cannot modify object class'¶
- errnum: ClassVar[int | None] = 69¶
- exception anyldap.ldap.NO_RESULTS_RETURNED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'No results returned'¶
- errnum: ClassVar[int | None] = -14¶
- exception anyldap.ldap.NO_SUCH_ATTRIBUTE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'No such attribute'¶
- errnum: ClassVar[int | None] = 16¶
- exception anyldap.ldap.NO_SUCH_OBJECT[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'No such object'¶
- errnum: ClassVar[int | None] = 32¶
- exception anyldap.ldap.NO_SUCH_OPERATION[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'No such operation'¶
- errnum: ClassVar[int | None] = 119¶
- exception anyldap.ldap.NO_UNIQUE_ENTRY[source]¶
Bases:
NO_SUCH_OBJECTA read expecting one entry found none, or several.
Not a result code a server sends: it is what this raises when a read that must find exactly one entry did not, which is what python-ldap raises it for too.
- desc: ClassVar[str] = 'No or non-unique search result'¶
- exception anyldap.ldap.OBJECT_CLASS_VIOLATION[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Object class violation'¶
- errnum: ClassVar[int | None] = 65¶
- exception anyldap.ldap.OPERATIONS_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Operations error'¶
- errnum: ClassVar[int | None] = 1¶
- exception anyldap.ldap.OTHER[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Other'¶
- errnum: ClassVar[int | None] = 80¶
- exception anyldap.ldap.PARAM_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Bad parameter to an ldap routine'¶
- errnum: ClassVar[int | None] = -9¶
- exception anyldap.ldap.PARTIAL_RESULTS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Partial results and referral received'¶
- errnum: ClassVar[int | None] = 9¶
- exception anyldap.ldap.PROTOCOL_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Protocol error'¶
- errnum: ClassVar[int | None] = 2¶
- exception anyldap.ldap.PROXIED_AUTHORIZATION_DENIED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Proxied authorization denied'¶
- errnum: ClassVar[int | None] = 123¶
- exception anyldap.ldap.REFERRAL[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Referral'¶
- errnum: ClassVar[int | None] = 10¶
- exception anyldap.ldap.REFERRAL_LIMIT_EXCEEDED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Referral limit exceeded'¶
- errnum: ClassVar[int | None] = -17¶
- exception anyldap.ldap.RESULTS_TOO_LARGE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Results too large'¶
- errnum: ClassVar[int | None] = 70¶
- class anyldap.ldap.ReconnectLDAPObject(uri: str, trace_level: int = 0, *, ssl_context: SSLContext | None = None, retry_max: int = 1, retry_delay: float = 60.0)[source]¶
Bases:
SimpleLDAPObjectA connection that opens itself again when the server goes away.
Each operation is tried, and if it fails with one of the errors that mean the connection rather than the request –
SERVER_DOWN,UNAVAILABLE,CONNECT_ERRORorTIMEOUT– the connection is made again and the operation tried once more. Reconnecting replays what was done to the connection before: the options that were set, StartTLS if it was raised, and the last bind that was made.retry_maxis how many times to try opening it again before giving up, andretry_delayhow long to wait between tries.Unlike python-ldap’s, this does not open the connection before the first operation: nothing is sent until something is awaited, so there is nothing to reconnect until an operation has failed.
- async extop_s(*args: Any, **kwargs: Any) Any[source]¶
Send an extended operation and wait for what it answers.
Given
extop_resp_class, the answer is read into one of those rather than handed back as the name and the bytes; a server that answers to a different name than the class expects is refused.
- async reconnect(uri: str, retry_max: int = 1, retry_delay: float = 60.0, force: bool = True) None[source]¶
Open the connection again, and put it back as it was.
- async sasl_bind_s(*args: Any, **kwargs: Any) Any[source]¶
One step of a SASL bind, for callers driving the exchange itself.
Answers with the credentials the server sent back, which is what the next step is built from.
- async sasl_interactive_bind_s(*args: Any, **kwargs: Any) Any[source]¶
Bind with a SASL mechanism, answering the server until it is done.
The mechanism is asked what to send, and asked again with whatever the server sent back, for as long as the server says the bind is still in progress. What the
OPT_X_SASL_*options say fills in whatever the mechanism was not told directly.
- exception anyldap.ldap.SASL_BIND_IN_PROGRESS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'SASL bind in progress'¶
- errnum: ClassVar[int | None] = 14¶
- exception anyldap.ldap.SERVER_DOWN[source]¶
Bases:
LDAPErrorThe connection could not be opened, or was lost.
- desc: ClassVar[str] = "Can't contact LDAP server"¶
- errnum: ClassVar[int | None] = -1¶
- exception anyldap.ldap.SIZELIMIT_EXCEEDED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Size limit exceeded'¶
- errnum: ClassVar[int | None] = 4¶
- anyldap.ldap.STRONG_AUTH_NOT_SUPPORTED¶
alias of
AUTH_METHOD_NOT_SUPPORTED
- exception anyldap.ldap.STRONG_AUTH_REQUIRED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Strong(er) authentication required'¶
- errnum: ClassVar[int | None] = 8¶
- exception anyldap.ldap.SUCCESS[source]¶
Bases:
LDAPErrorThe code that is not an error; nothing here is raised with it.
- desc: ClassVar[str] = 'Success'¶
- errnum: ClassVar[int | None] = 0¶
- class anyldap.ldap.SimpleLDAPObject(uri: str, trace_level: int = 0, *, ssl_context: SSLContext | None = None)[source]¶
Bases:
objectA connection to one LDAP server, with the API of python-ldap’s own.
The connection is opened by the first operation that needs it, so that
initialize()stays the plain call python-ldap makes it. Closing it isunbind_s(), andasync withcloses it however the block ends.- CLASSATTR_OPTION_MAPPING: ClassVar[dict[str, int]] = {'deref': 2, 'network_timeout': 20485, 'protocol_version': 17, 'referrals': 8, 'sizelimit': 3, 'timelimit': 4}¶
- async abandon(msgid: int, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None[source]¶
- async abandon_ext(msgid: int, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None[source]¶
Tell the server to stop working on an operation, and forget it.
Nothing is answered: an abandon is not a request the server replies to, which is what
cancel()is for.
- async add_ext(dn: str, modlist: Sequence[tuple[str, Sequence[str | bytes] | str | bytes]], serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async add_ext_s(dn: str, modlist: Sequence[tuple[str, Sequence[str | bytes] | str | bytes]], serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async add_s(dn: str, modlist: Sequence[tuple[str, Sequence[str | bytes] | str | bytes]]) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async bind_s(who: str, cred: str | bytes, method: int = 128) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async cancel(cancelid: int, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
Ask the server to stop working on an operation (RFC 3909).
Unlike
abandon()this is answered, so the caller learns whether the operation really did stop; the operation itself finishes withCANCELLED.
- async cancel_s(cancelid: int, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]]] | None[source]¶
- async compare_ext(dn: str, attr: str, value: str | bytes, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async compare_ext_s(dn: str, attr: str, value: str | bytes, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) bool[source]¶
- async delete_ext(dn: str, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async delete_ext_s(dn: str, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async delete_s(dn: str) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async extop(extreq: LDAPExtendedRequest, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async extop_result(msgid: int = -1, all: int = 1, timeout: float | None = None) tuple[str | None, bytes | None][source]¶
Collect an extended operation started with
extop().
- async extop_s(extreq: LDAPExtendedRequest, serverctrls: Iterable[controls.RequestControl | pureldap.Control] | None = None, clientctrls: Iterable[controls.RequestControl | pureldap.Control] | None = None, extop_resp_class: type[ExtendedResponse] | None = None) tuple[str | None, bytes | None] | ExtendedResponse[source]¶
Send an extended operation and wait for what it answers.
Given
extop_resp_class, the answer is read into one of those rather than handed back as the name and the bytes; a server that answers to a different name than the class expects is refused.
- fileno() int[source]¶
The socket this connection is open on.
For a caller that wants to watch the connection with something of its own. It raises if nothing is open yet, because there is no socket to name until the first operation has been awaited.
- async find_unique_entry(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, timeout: float = -1) tuple[str, dict[str, list[bytes]]][source]¶
The one entry a search found, or an error saying it was not one.
The search asks for two entries, so a server that has more says so rather than sending them all.
- async modify(dn: str, modlist: Sequence[tuple[int, str, Sequence[str | bytes] | str | bytes | None]]) int[source]¶
- async modify_ext(dn: str, modlist: Sequence[tuple[int, str, Sequence[str | bytes] | str | bytes | None]], serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async modify_ext_s(dn: str, modlist: Sequence[tuple[int, str, Sequence[str | bytes] | str | bytes | None]], serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async modify_s(dn: str, modlist: Sequence[tuple[int, str, Sequence[str | bytes] | str | bytes | None]]) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async modrdn_s(dn: str, newrdn: str, delold: int = 1) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async passwd(user: str | bytes | None = None, oldpw: str | bytes | None = None, newpw: str | bytes | None = None, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async passwd_s(user: str | bytes | None = None, oldpw: str | bytes | None = None, newpw: str | bytes | None = None, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, extract_newpw: bool = False) tuple[str | None, bytes | None][source]¶
- async read_rootdse_s(filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None) dict[str, list[bytes]][source]¶
What the server says about itself, from its root DSE.
- async read_s(dn: str, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, timeout: float = -1) dict[str, list[bytes]] | None[source]¶
The attributes of one entry, or None when it is not there.
- async read_schema_s(dn: str = '') SubSchema[source]¶
The schema an entry is written against, read into objects.
Not a method python-ldap has: it fetches schema with
ldap.schema.urlfetch(), which opens its own connection. This uses the connection that is already here.
- async read_subschemasubentry_s(subschemasubentry_dn: str, attrs: Sequence[str] | None = None) dict[str, list[bytes]] | None[source]¶
The schema itself, as the server publishes it.
- async rename(dn: str, newrdn: str, newsuperior: str | None = None, delold: int = 1, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async rename_s(dn: str, newrdn: str, newsuperior: str | None = None, delold: int = 1, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async result(msgid: int = -1, all: int = 1, timeout: float | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]]][source]¶
- async result2(msgid: int = -1, all: int = 1, timeout: float | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int][source]¶
- async result3(msgid: int = -1, all: int = 1, timeout: float | None = None, resp_ctrl_classes: Mapping[str, type[ResponseControl]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
Wait for an operation started earlier, and answer with its result.
RES_ANYtakes the operation that was started first, which is the one python-ldap would most likely hand back.
- async result4(msgid: int = -1, all: int = 1, timeout: float | None = None, add_ctrls: int = 0, add_intermediates: int = 0, add_extop: int = 0, resp_ctrl_classes: Mapping[str, type[ResponseControl]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]] | tuple[str | None, bytes | None] | tuple[str, dict[str, list[bytes]], Sequence[ResponseControl]] | tuple[None, list[str], Sequence[ResponseControl]] | tuple[str | None, bytes | None, Sequence[ResponseControl]]], int, Sequence[ResponseControl], str | None, bytes | None][source]¶
- async sasl_bind_s(dn: str, mechanism: str | bytes, cred: str | bytes | None, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) bytes | None[source]¶
One step of a SASL bind, for callers driving the exchange itself.
Answers with the credentials the server sent back, which is what the next step is built from.
- async sasl_external_bind_s(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, sasl_flags: int = 2, authz_id: str = '') None[source]¶
Bind as whoever the connection underneath already proved to be.
- async sasl_gssapi_bind_s(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, sasl_flags: int = 2, authz_id: str = '') None[source]¶
Bind with Kerberos, which is what the GSSAPI mechanism is.
- async sasl_interactive_bind_s(who: str, auth: sasl, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, sasl_flags: int = 2) None[source]¶
Bind with a SASL mechanism, answering the server until it is done.
The mechanism is asked what to send, and asked again with whatever the server sent back, for as long as the server says the bind is still in progress. What the
OPT_X_SASL_*options say fills in whatever the mechanism was not told directly.
- async sasl_non_interactive_bind_s(sasl_mech: str, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, sasl_flags: int = 2, authz_id: str = '') None[source]¶
A SASL bind with a mechanism that asks the caller nothing.
- async search(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0) int[source]¶
- async search_ext(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, timeout: float = -1, sizelimit: int = 0) int[source]¶
- async search_ext_s(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, timeout: float = -1, sizelimit: int = 0) list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]][source]¶
- async search_s(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0) list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]][source]¶
- async search_st(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0, timeout: float = -1) list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]][source]¶
- async search_subschemasubentry_s(dn: str = '') str | None[source]¶
Where the server keeps the schema an entry is written against.
- async simple_bind(who: str = '', cred: str | bytes = '', serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async simple_bind_s(who: str = '', cred: str | bytes = '', serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async start_tls_s() None[source]¶
Raise TLS on the connection, as StartTLS does.
Nothing else is reading the connection, so the handshake follows the response on the same stream, with no request in flight to confuse.
- async unbind(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None¶
Say goodbye to the server and close the connection.
- async unbind_ext(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None[source]¶
Say goodbye to the server and close the connection.
- async unbind_ext_s(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None¶
Say goodbye to the server and close the connection.
- async unbind_s(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None¶
Say goodbye to the server and close the connection.
- async whoami_s(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) str[source]¶
The identity the server has this connection bound as.
- exception anyldap.ldap.TIMELIMIT_EXCEEDED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Time limit exceeded'¶
- errnum: ClassVar[int | None] = 3¶
- exception anyldap.ldap.TIMEOUT[source]¶
Bases:
LDAPErrorThe client gave up waiting before the server answered.
- desc: ClassVar[str] = 'Timed out'¶
- errnum: ClassVar[int | None] = -5¶
- exception anyldap.ldap.TOO_LATE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Too late'¶
- errnum: ClassVar[int | None] = 120¶
- exception anyldap.ldap.TYPE_OR_VALUE_EXISTS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Type or value exists'¶
- errnum: ClassVar[int | None] = 20¶
- exception anyldap.ldap.UNAVAILABLE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Server is unavailable'¶
- errnum: ClassVar[int | None] = 52¶
- exception anyldap.ldap.UNAVAILABLE_CRITICAL_EXTENSION[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Critical extension is unavailable'¶
- errnum: ClassVar[int | None] = 12¶
- exception anyldap.ldap.UNDEFINED_TYPE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Undefined attribute type'¶
- errnum: ClassVar[int | None] = 17¶
- exception anyldap.ldap.UNWILLING_TO_PERFORM[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Server is unwilling to perform'¶
- errnum: ClassVar[int | None] = 53¶
- exception anyldap.ldap.USER_CANCELLED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'User cancelled operation'¶
- errnum: ClassVar[int | None] = -8¶
- exception anyldap.ldap.VLV_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Virtual List View error'¶
- errnum: ClassVar[int | None] = 76¶
- exception anyldap.ldap.X_PROXY_AUTHZ_FAILURE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Proxy authorization failure'¶
- errnum: ClassVar[int | None] = 47¶
- anyldap.ldap.addModlist(entry: Mapping[str, Sequence[str | bytes | None] | str | bytes | None], ignore_attr_types: Iterable[str] = ()) Sequence[tuple[str, Sequence[str | bytes] | str | bytes]][source]¶
The modlist that adds an entry, skipping attributes with no values.
- anyldap.ldap.dn2str(dn: Sequence[Sequence[tuple[str, str, int]]]) str[source]¶
The text of a DN that was taken apart by str2dn().
- anyldap.ldap.error_for_result(code: int, message: str | bytes | None = None, **fields: object) LDAPError[source]¶
The exception a result code stands for, carrying the server’s message.
Anything else the response said about itself – which message it was, what kind, the controls it carried – goes in the same dictionary, where python-ldap puts it.
- anyldap.ldap.escape_filter_chars(assertion_value: str | bytes, escape_mode: int = 0) str[source]¶
An assertion value with the characters a filter would read escaped.
escape_mode0 escapes what RFC 4515 requires, 1 escapes everything outside the printable range as well, and 2 escapes every character.Each mode escapes what python-ldap’s own escapes, character by character as it does, so a filter built here is the one it would have built.
- anyldap.ldap.escape_str(escape_func: Callable[[_Escapable], str], val: str, *args: _Escapable) str[source]¶
A template filled in with values that were escaped first.
escape_funcis what to escape them with:escape_filter_charsfor a filter,escape_dn_charsfor a DN.
- anyldap.ldap.explode_dn(dn: str | bytes, notypes: int = 0, flags: int = 0) list[str][source]¶
The RDNs of a DN, as text, optionally without their attribute types.
- anyldap.ldap.explode_rdn(rdn: str | bytes, notypes: int = 0, flags: int = 0) list[str][source]¶
The attribute/value pairs of one RDN, as text.
- anyldap.ldap.filter_format(filter_template: str, assertion_values: Iterable[str | bytes]) str[source]¶
A filter built from a template, with every value escaped first.
- anyldap.ldap.get_option(option: int) object[source]¶
What a connection opened from now on would start with.
- anyldap.ldap.initialize(uri: str, trace_level: int = 0, *, ssl_context: SSLContext | None = None) SimpleLDAPObject[source]¶
A connection to the server named by an
ldap://orldaps://URL.Nothing is sent until the first operation is awaited, which is what lets this stay the plain call python-ldap makes it. Whatever was given to
ldap.set_option()is what the connection starts with.
- anyldap.ldap.is_dn(s: str, flags: int = 0) bool[source]¶
Whether this is a distinguished name that can be parsed.
- anyldap.ldap.modifyModlist(old_entry: Mapping[str, Sequence[str | bytes | None] | str | bytes | None], new_entry: Mapping[str, Sequence[str | bytes | None] | str | bytes | None], ignore_attr_types: Iterable[str] = (), ignore_oldexistent: int = 0, case_ignore_attr_types: Iterable[str] = ()) Sequence[tuple[int, str, Sequence[str | bytes] | str | bytes | None]][source]¶
The modlist that turns one entry into another.
An attribute whose values changed is deleted and added again rather than replaced, which is what python-ldap’s own does: the server then rejects the change if the entry moved underneath, instead of overwriting it.
- anyldap.ldap.open(host: str, port: int = 389, trace_level: int = 0) SimpleLDAPObject[source]¶
- anyldap.ldap.set_option(option: int, invalue: object) None[source]¶
Set an option on every connection opened from now on.
A connection that is already open keeps what it has; set the option on the connection itself to change that one.
- anyldap.ldap.str2dn(dn: str | bytes | None, flags: int = 0) list[list[tuple[str, str, int]]][source]¶
A DN taken apart into its RDNs, each a list of attribute/value pairs.
Raises
DECODING_ERRORfor text that is not a distinguished name, which is what python-ldap raises for it.
- anyldap.ldap.strf_secs(secs: float) str[source]¶
Seconds since the epoch, as the generalized time LDAP writes.
- anyldap.ldap.strp_secs(dt_str: str) int[source]¶
A generalized time, as seconds since the epoch.
The time read is UTC, so it is turned back into seconds as UTC. python-ldap goes through
time.mktime()and subtractstime.timezone, which is an hour out wherever the local zone is on summer time; this is the inverse ofstrf_secs()everywhere.
- anyldap.ldap.time_span_filter(filterstr: str = '', from_timestamp: float = 0, until_timestamp: float | None = None, delta_attr: str = 'modifyTimestamp') str[source]¶
This filter, narrowed to what changed between two times.
A negative
from_timestampis that many seconds before the end of the span, which is now unlessuntil_timestampsays otherwise.
anyldap.ldap.ldapobject module¶
An LDAP connection with python-ldap’s API, awaited rather than blocking.
Every method python-ldap spells synchronously is a coroutine here; the
arguments it takes and the values it hands back are the ones python-ldap
documents, so code ports by adding await.
Like python-ldap, this reads the connection only while an operation is being
waited for: an operation is started by writing its request and remembering
its message id, and result3() reads answers off the socket, handing each
to whichever operation it belongs to, until the one asked for is finished.
Nothing runs in the background, so a connection needs no task group and can
be used from whichever task has it.
- anyldap.ldap.ldapobject.LDAPObject¶
alias of
SimpleLDAPObject
- class anyldap.ldap.ldapobject.ReconnectLDAPObject(uri: str, trace_level: int = 0, *, ssl_context: SSLContext | None = None, retry_max: int = 1, retry_delay: float = 60.0)[source]¶
Bases:
SimpleLDAPObjectA connection that opens itself again when the server goes away.
Each operation is tried, and if it fails with one of the errors that mean the connection rather than the request –
SERVER_DOWN,UNAVAILABLE,CONNECT_ERRORorTIMEOUT– the connection is made again and the operation tried once more. Reconnecting replays what was done to the connection before: the options that were set, StartTLS if it was raised, and the last bind that was made.retry_maxis how many times to try opening it again before giving up, andretry_delayhow long to wait between tries.Unlike python-ldap’s, this does not open the connection before the first operation: nothing is sent until something is awaited, so there is nothing to reconnect until an operation has failed.
- async extop_s(*args: Any, **kwargs: Any) Any[source]¶
Send an extended operation and wait for what it answers.
Given
extop_resp_class, the answer is read into one of those rather than handed back as the name and the bytes; a server that answers to a different name than the class expects is refused.
- async reconnect(uri: str, retry_max: int = 1, retry_delay: float = 60.0, force: bool = True) None[source]¶
Open the connection again, and put it back as it was.
- async sasl_bind_s(*args: Any, **kwargs: Any) Any[source]¶
One step of a SASL bind, for callers driving the exchange itself.
Answers with the credentials the server sent back, which is what the next step is built from.
- async sasl_interactive_bind_s(*args: Any, **kwargs: Any) Any[source]¶
Bind with a SASL mechanism, answering the server until it is done.
The mechanism is asked what to send, and asked again with whatever the server sent back, for as long as the server says the bind is still in progress. What the
OPT_X_SASL_*options say fills in whatever the mechanism was not told directly.
- class anyldap.ldap.ldapobject.SimpleLDAPObject(uri: str, trace_level: int = 0, *, ssl_context: SSLContext | None = None)[source]¶
Bases:
objectA connection to one LDAP server, with the API of python-ldap’s own.
The connection is opened by the first operation that needs it, so that
initialize()stays the plain call python-ldap makes it. Closing it isunbind_s(), andasync withcloses it however the block ends.- CLASSATTR_OPTION_MAPPING: ClassVar[dict[str, int]] = {'deref': 2, 'network_timeout': 20485, 'protocol_version': 17, 'referrals': 8, 'sizelimit': 3, 'timelimit': 4}¶
- async abandon(msgid: int, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None[source]¶
- async abandon_ext(msgid: int, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None[source]¶
Tell the server to stop working on an operation, and forget it.
Nothing is answered: an abandon is not a request the server replies to, which is what
cancel()is for.
- async add_ext(dn: str, modlist: Sequence[tuple[str, Sequence[str | bytes] | str | bytes]], serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async add_ext_s(dn: str, modlist: Sequence[tuple[str, Sequence[str | bytes] | str | bytes]], serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async add_s(dn: str, modlist: Sequence[tuple[str, Sequence[str | bytes] | str | bytes]]) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async bind_s(who: str, cred: str | bytes, method: int = 128) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async cancel(cancelid: int, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
Ask the server to stop working on an operation (RFC 3909).
Unlike
abandon()this is answered, so the caller learns whether the operation really did stop; the operation itself finishes withCANCELLED.
- async cancel_s(cancelid: int, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]]] | None[source]¶
- async compare_ext(dn: str, attr: str, value: str | bytes, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async compare_ext_s(dn: str, attr: str, value: str | bytes, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) bool[source]¶
- async delete_ext(dn: str, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async delete_ext_s(dn: str, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async delete_s(dn: str) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async extop(extreq: LDAPExtendedRequest, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async extop_result(msgid: int = -1, all: int = 1, timeout: float | None = None) tuple[str | None, bytes | None][source]¶
Collect an extended operation started with
extop().
- async extop_s(extreq: LDAPExtendedRequest, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, extop_resp_class: None = None) tuple[str | None, bytes | None][source]¶
- async extop_s(extreq: LDAPExtendedRequest, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None, extop_resp_class: type[_Response]) _Response
Send an extended operation and wait for what it answers.
Given
extop_resp_class, the answer is read into one of those rather than handed back as the name and the bytes; a server that answers to a different name than the class expects is refused.
- fileno() int[source]¶
The socket this connection is open on.
For a caller that wants to watch the connection with something of its own. It raises if nothing is open yet, because there is no socket to name until the first operation has been awaited.
- async find_unique_entry(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, timeout: float = -1) tuple[str, dict[str, list[bytes]]][source]¶
The one entry a search found, or an error saying it was not one.
The search asks for two entries, so a server that has more says so rather than sending them all.
- async modify(dn: str, modlist: Sequence[tuple[int, str, Sequence[str | bytes] | str | bytes | None]]) int[source]¶
- async modify_ext(dn: str, modlist: Sequence[tuple[int, str, Sequence[str | bytes] | str | bytes | None]], serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async modify_ext_s(dn: str, modlist: Sequence[tuple[int, str, Sequence[str | bytes] | str | bytes | None]], serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async modify_s(dn: str, modlist: Sequence[tuple[int, str, Sequence[str | bytes] | str | bytes | None]]) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async modrdn_s(dn: str, newrdn: str, delold: int = 1) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- network_timeout: float | None¶
- async passwd(user: str | bytes | None = None, oldpw: str | bytes | None = None, newpw: str | bytes | None = None, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async passwd_s(user: str | bytes | None = None, oldpw: str | bytes | None = None, newpw: str | bytes | None = None, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, extract_newpw: bool = False) tuple[str | None, bytes | None][source]¶
- async read_rootdse_s(filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None) dict[str, list[bytes]][source]¶
What the server says about itself, from its root DSE.
- async read_s(dn: str, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, timeout: float = -1) dict[str, list[bytes]] | None[source]¶
The attributes of one entry, or None when it is not there.
- async read_schema_s(dn: str = '') SubSchema[source]¶
The schema an entry is written against, read into objects.
Not a method python-ldap has: it fetches schema with
ldap.schema.urlfetch(), which opens its own connection. This uses the connection that is already here.
- async read_subschemasubentry_s(subschemasubentry_dn: str, attrs: Sequence[str] | None = None) dict[str, list[bytes]] | None[source]¶
The schema itself, as the server publishes it.
- async rename(dn: str, newrdn: str, newsuperior: str | None = None, delold: int = 1, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async rename_s(dn: str, newrdn: str, newsuperior: str | None = None, delold: int = 1, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async result(msgid: int = -1, all: int = 1, timeout: float | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]]][source]¶
- async result2(msgid: int = -1, all: int = 1, timeout: float | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int][source]¶
- async result3(msgid: int = -1, all: int = 1, timeout: float | None = None, resp_ctrl_classes: Mapping[str, type[ResponseControl]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
Wait for an operation started earlier, and answer with its result.
RES_ANYtakes the operation that was started first, which is the one python-ldap would most likely hand back.
- async result4(msgid: int = -1, all: int = 1, timeout: float | None = None, add_ctrls: int = 0, add_intermediates: int = 0, add_extop: int = 0, resp_ctrl_classes: Mapping[str, type[ResponseControl]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]] | tuple[str | None, bytes | None] | tuple[str, dict[str, list[bytes]], Sequence[ResponseControl]] | tuple[None, list[str], Sequence[ResponseControl]] | tuple[str | None, bytes | None, Sequence[ResponseControl]]], int, Sequence[ResponseControl], str | None, bytes | None][source]¶
- async sasl_bind_s(dn: str, mechanism: str | bytes, cred: str | bytes | None, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) bytes | None[source]¶
One step of a SASL bind, for callers driving the exchange itself.
Answers with the credentials the server sent back, which is what the next step is built from.
- async sasl_external_bind_s(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, sasl_flags: int = 2, authz_id: str = '') None[source]¶
Bind as whoever the connection underneath already proved to be.
- async sasl_gssapi_bind_s(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, sasl_flags: int = 2, authz_id: str = '') None[source]¶
Bind with Kerberos, which is what the GSSAPI mechanism is.
- async sasl_interactive_bind_s(who: str, auth: sasl, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, sasl_flags: int = 2) None[source]¶
Bind with a SASL mechanism, answering the server until it is done.
The mechanism is asked what to send, and asked again with whatever the server sent back, for as long as the server says the bind is still in progress. What the
OPT_X_SASL_*options say fills in whatever the mechanism was not told directly.
- async sasl_non_interactive_bind_s(sasl_mech: str, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, sasl_flags: int = 2, authz_id: str = '') None[source]¶
A SASL bind with a mechanism that asks the caller nothing.
- async search(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0) int[source]¶
- async search_ext(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, timeout: float = -1, sizelimit: int = 0) int[source]¶
- async search_ext_s(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0, serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, timeout: float = -1, sizelimit: int = 0) list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]][source]¶
- async search_s(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0) list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]][source]¶
- async search_st(base: str, scope: int = 2, filterstr: str = '(objectClass=*)', attrlist: Sequence[str] | None = None, attrsonly: int = 0, timeout: float = -1) list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]][source]¶
- async search_subschemasubentry_s(dn: str = '') str | None[source]¶
Where the server keeps the schema an entry is written against.
- async simple_bind(who: str = '', cred: str | bytes = '', serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) int[source]¶
- async simple_bind_s(who: str = '', cred: str | bytes = '', serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) tuple[int, list[tuple[str, dict[str, list[bytes]]] | tuple[None, list[str]]], int, Sequence[ResponseControl]][source]¶
- async start_tls_s() None[source]¶
Raise TLS on the connection, as StartTLS does.
Nothing else is reading the connection, so the handshake follows the response on the same stream, with no request in flight to confuse.
- timeout: float | None¶
- async unbind(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None¶
Say goodbye to the server and close the connection.
- async unbind_ext(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None[source]¶
Say goodbye to the server and close the connection.
- async unbind_ext_s(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None¶
Say goodbye to the server and close the connection.
- async unbind_s(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) None¶
Say goodbye to the server and close the connection.
- async whoami_s(serverctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None, clientctrls: Iterable[RequestControl | tuple[str | bytes, int | None, str | bytes | None]] | None = None) str[source]¶
The identity the server has this connection bound as.
- anyldap.ldap.ldapobject.initialize(uri: str, trace_level: int = 0, *, ssl_context: SSLContext | None = None) SimpleLDAPObject[source]¶
A connection to the server named by an
ldap://orldaps://URL.Nothing is sent until the first operation is awaited, which is what lets this stay the plain call python-ldap makes it. Whatever was given to
ldap.set_option()is what the connection starts with.
- anyldap.ldap.ldapobject.open(host: str, port: int = 389, trace_level: int = 0) SimpleLDAPObject[source]¶
anyldap.ldap.errors module¶
The exceptions python-ldap raises, keyed by the result code that causes them.
Each class carries the LDAP result code it stands for, and an instance holds
the same args[0] dictionary python-ldap builds: a desc describing the
code, and the server’s own info message when it sent one.
- exception anyldap.ldap.errors.ADMINLIMIT_EXCEEDED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Administrative limit exceeded'¶
- errnum: ClassVar[int | None] = 11¶
- anyldap.ldap.errors.ADMIN_LIMIT_EXCEEDED¶
alias of
ADMINLIMIT_EXCEEDED
- exception anyldap.ldap.errors.AFFECTS_MULTIPLE_DSAS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Operation affects multiple DSAs'¶
- errnum: ClassVar[int | None] = 71¶
- exception anyldap.ldap.errors.ALIAS_DEREF_PROBLEM[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Alias dereferencing problem'¶
- errnum: ClassVar[int | None] = 36¶
- exception anyldap.ldap.errors.ALIAS_PROBLEM[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Alias problem'¶
- errnum: ClassVar[int | None] = 33¶
- exception anyldap.ldap.errors.ALREADY_EXISTS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Already exists'¶
- errnum: ClassVar[int | None] = 68¶
- exception anyldap.ldap.errors.ASSERTION_FAILED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Assertion failed'¶
- errnum: ClassVar[int | None] = 122¶
- exception anyldap.ldap.errors.AUTH_METHOD_NOT_SUPPORTED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Authentication method not supported'¶
- errnum: ClassVar[int | None] = 7¶
- exception anyldap.ldap.errors.AUTH_UNKNOWN[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Unknown authentication method'¶
- errnum: ClassVar[int | None] = -6¶
- exception anyldap.ldap.errors.BUSY[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Server is busy'¶
- errnum: ClassVar[int | None] = 51¶
- exception anyldap.ldap.errors.CANCELLED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Cancelled'¶
- errnum: ClassVar[int | None] = 118¶
- exception anyldap.ldap.errors.CANNOT_CANCEL[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Cannot cancel'¶
- errnum: ClassVar[int | None] = 121¶
- exception anyldap.ldap.errors.CLIENT_LOOP[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Client loop'¶
- errnum: ClassVar[int | None] = -16¶
- exception anyldap.ldap.errors.COMPARE_FALSE[source]¶
Bases:
LDAPErrorA compare that answered no; compare_s() turns this into False.
- desc: ClassVar[str] = 'Compare False'¶
- errnum: ClassVar[int | None] = 5¶
- exception anyldap.ldap.errors.COMPARE_TRUE[source]¶
Bases:
LDAPErrorA compare that answered yes; compare_s() turns this into True.
- desc: ClassVar[str] = 'Compare True'¶
- errnum: ClassVar[int | None] = 6¶
- exception anyldap.ldap.errors.CONFIDENTIALITY_REQUIRED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Confidentiality required'¶
- errnum: ClassVar[int | None] = 13¶
- exception anyldap.ldap.errors.CONNECT_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Connect error'¶
- errnum: ClassVar[int | None] = -11¶
- exception anyldap.ldap.errors.CONSTRAINT_VIOLATION[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Constraint violation'¶
- errnum: ClassVar[int | None] = 19¶
- exception anyldap.ldap.errors.CONTROL_NOT_FOUND[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Control not found'¶
- errnum: ClassVar[int | None] = -13¶
- exception anyldap.ldap.errors.DECODING_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Decoding error'¶
- errnum: ClassVar[int | None] = -4¶
- exception anyldap.ldap.errors.ENCODING_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Encoding error'¶
- errnum: ClassVar[int | None] = -3¶
- exception anyldap.ldap.errors.FILTER_ERROR[source]¶
Bases:
LDAPErrorThe search filter could not be parsed.
- desc: ClassVar[str] = 'Bad search filter'¶
- errnum: ClassVar[int | None] = -7¶
- exception anyldap.ldap.errors.INAPPROPRIATE_AUTH[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Inappropriate authentication'¶
- errnum: ClassVar[int | None] = 48¶
- exception anyldap.ldap.errors.INAPPROPRIATE_MATCHING[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Inappropriate matching'¶
- errnum: ClassVar[int | None] = 18¶
- exception anyldap.ldap.errors.INSUFFICIENT_ACCESS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Insufficient access'¶
- errnum: ClassVar[int | None] = 50¶
- exception anyldap.ldap.errors.INVALID_CREDENTIALS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Invalid credentials'¶
- errnum: ClassVar[int | None] = 49¶
- exception anyldap.ldap.errors.INVALID_DN_SYNTAX[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Invalid DN syntax'¶
- errnum: ClassVar[int | None] = 34¶
- exception anyldap.ldap.errors.INVALID_SYNTAX[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Invalid syntax'¶
- errnum: ClassVar[int | None] = 21¶
- exception anyldap.ldap.errors.IS_LEAF[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Is a leaf'¶
- errnum: ClassVar[int | None] = 35¶
- exception anyldap.ldap.errors.LDAPError[source]¶
Bases:
ExceptionThe base of every error raised here, as
ldap.LDAPErroris.- desc: ClassVar[str] = 'Unknown error'¶
- errnum: ClassVar[int | None] = None¶
- property info: str | None¶
The diagnostic message the server sent, if it sent one.
- exception anyldap.ldap.errors.LOCAL_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Local error'¶
- errnum: ClassVar[int | None] = -2¶
- exception anyldap.ldap.errors.LOOP_DETECT[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Loop detected'¶
- errnum: ClassVar[int | None] = 54¶
- exception anyldap.ldap.errors.MORE_RESULTS_TO_RETURN[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'More results to return'¶
- errnum: ClassVar[int | None] = -15¶
- exception anyldap.ldap.errors.NAMING_VIOLATION[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Naming violation'¶
- errnum: ClassVar[int | None] = 64¶
- exception anyldap.ldap.errors.NOT_ALLOWED_ON_NONLEAF[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Operation not allowed on non-leaf'¶
- errnum: ClassVar[int | None] = 66¶
- exception anyldap.ldap.errors.NOT_ALLOWED_ON_RDN[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Operation not allowed on RDN'¶
- errnum: ClassVar[int | None] = 67¶
- exception anyldap.ldap.errors.NOT_SUPPORTED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Not supported'¶
- errnum: ClassVar[int | None] = -12¶
- exception anyldap.ldap.errors.NO_MEMORY[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Out of memory'¶
- errnum: ClassVar[int | None] = -10¶
- exception anyldap.ldap.errors.NO_OBJECT_CLASS_MODS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Cannot modify object class'¶
- errnum: ClassVar[int | None] = 69¶
- exception anyldap.ldap.errors.NO_RESULTS_RETURNED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'No results returned'¶
- errnum: ClassVar[int | None] = -14¶
- exception anyldap.ldap.errors.NO_SUCH_ATTRIBUTE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'No such attribute'¶
- errnum: ClassVar[int | None] = 16¶
- exception anyldap.ldap.errors.NO_SUCH_OBJECT[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'No such object'¶
- errnum: ClassVar[int | None] = 32¶
- exception anyldap.ldap.errors.NO_SUCH_OPERATION[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'No such operation'¶
- errnum: ClassVar[int | None] = 119¶
- exception anyldap.ldap.errors.NO_UNIQUE_ENTRY[source]¶
Bases:
NO_SUCH_OBJECTA read expecting one entry found none, or several.
Not a result code a server sends: it is what this raises when a read that must find exactly one entry did not, which is what python-ldap raises it for too.
- desc: ClassVar[str] = 'No or non-unique search result'¶
- exception anyldap.ldap.errors.OBJECT_CLASS_VIOLATION[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Object class violation'¶
- errnum: ClassVar[int | None] = 65¶
- exception anyldap.ldap.errors.OPERATIONS_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Operations error'¶
- errnum: ClassVar[int | None] = 1¶
- exception anyldap.ldap.errors.OTHER[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Other'¶
- errnum: ClassVar[int | None] = 80¶
- exception anyldap.ldap.errors.PARAM_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Bad parameter to an ldap routine'¶
- errnum: ClassVar[int | None] = -9¶
- exception anyldap.ldap.errors.PARTIAL_RESULTS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Partial results and referral received'¶
- errnum: ClassVar[int | None] = 9¶
- exception anyldap.ldap.errors.PROTOCOL_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Protocol error'¶
- errnum: ClassVar[int | None] = 2¶
- exception anyldap.ldap.errors.PROXIED_AUTHORIZATION_DENIED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Proxied authorization denied'¶
- errnum: ClassVar[int | None] = 123¶
- exception anyldap.ldap.errors.REFERRAL[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Referral'¶
- errnum: ClassVar[int | None] = 10¶
- exception anyldap.ldap.errors.REFERRAL_LIMIT_EXCEEDED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Referral limit exceeded'¶
- errnum: ClassVar[int | None] = -17¶
- exception anyldap.ldap.errors.RESULTS_TOO_LARGE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Results too large'¶
- errnum: ClassVar[int | None] = 70¶
- exception anyldap.ldap.errors.SASL_BIND_IN_PROGRESS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'SASL bind in progress'¶
- errnum: ClassVar[int | None] = 14¶
- exception anyldap.ldap.errors.SERVER_DOWN[source]¶
Bases:
LDAPErrorThe connection could not be opened, or was lost.
- desc: ClassVar[str] = "Can't contact LDAP server"¶
- errnum: ClassVar[int | None] = -1¶
- exception anyldap.ldap.errors.SIZELIMIT_EXCEEDED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Size limit exceeded'¶
- errnum: ClassVar[int | None] = 4¶
- anyldap.ldap.errors.STRONG_AUTH_NOT_SUPPORTED¶
alias of
AUTH_METHOD_NOT_SUPPORTED
- exception anyldap.ldap.errors.STRONG_AUTH_REQUIRED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Strong(er) authentication required'¶
- errnum: ClassVar[int | None] = 8¶
- exception anyldap.ldap.errors.SUCCESS[source]¶
Bases:
LDAPErrorThe code that is not an error; nothing here is raised with it.
- desc: ClassVar[str] = 'Success'¶
- errnum: ClassVar[int | None] = 0¶
- exception anyldap.ldap.errors.TIMELIMIT_EXCEEDED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Time limit exceeded'¶
- errnum: ClassVar[int | None] = 3¶
- exception anyldap.ldap.errors.TIMEOUT[source]¶
Bases:
LDAPErrorThe client gave up waiting before the server answered.
- desc: ClassVar[str] = 'Timed out'¶
- errnum: ClassVar[int | None] = -5¶
- exception anyldap.ldap.errors.TOO_LATE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Too late'¶
- errnum: ClassVar[int | None] = 120¶
- exception anyldap.ldap.errors.TYPE_OR_VALUE_EXISTS[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Type or value exists'¶
- errnum: ClassVar[int | None] = 20¶
- exception anyldap.ldap.errors.UNAVAILABLE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Server is unavailable'¶
- errnum: ClassVar[int | None] = 52¶
- exception anyldap.ldap.errors.UNAVAILABLE_CRITICAL_EXTENSION[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Critical extension is unavailable'¶
- errnum: ClassVar[int | None] = 12¶
- exception anyldap.ldap.errors.UNDEFINED_TYPE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Undefined attribute type'¶
- errnum: ClassVar[int | None] = 17¶
- exception anyldap.ldap.errors.UNWILLING_TO_PERFORM[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Server is unwilling to perform'¶
- errnum: ClassVar[int | None] = 53¶
- exception anyldap.ldap.errors.USER_CANCELLED[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'User cancelled operation'¶
- errnum: ClassVar[int | None] = -8¶
- exception anyldap.ldap.errors.VLV_ERROR[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Virtual List View error'¶
- errnum: ClassVar[int | None] = 76¶
- exception anyldap.ldap.errors.X_PROXY_AUTHZ_FAILURE[source]¶
Bases:
LDAPError- desc: ClassVar[str] = 'Proxy authorization failure'¶
- errnum: ClassVar[int | None] = 47¶
- anyldap.ldap.errors.error_for_result(code: int, message: str | bytes | None = None, **fields: object) LDAPError[source]¶
The exception a result code stands for, carrying the server’s message.
Anything else the response said about itself – which message it was, what kind, the controls it carried – goes in the same dictionary, where python-ldap puts it.
anyldap.ldap.constants module¶
The names and values python-ldap’s ldap module defines.
Only the constants the client honours are here. An option python-ldap has but this cannot act on is left out, so that setting it fails loudly rather than being quietly ignored.
anyldap.ldap.dn module¶
ldap.dn: taking distinguished names apart and putting them together.
- anyldap.ldap.dn.dn2str(dn: Sequence[Sequence[tuple[str, str, int]]]) str[source]¶
The text of a DN that was taken apart by str2dn().
- anyldap.ldap.dn.escape_dn_chars(s: str) str[source]¶
A value with the characters a DN would read escaped.
- anyldap.ldap.dn.explode_dn(dn: str | bytes, notypes: int = 0, flags: int = 0) list[str][source]¶
The RDNs of a DN, as text, optionally without their attribute types.
- anyldap.ldap.dn.explode_rdn(rdn: str | bytes, notypes: int = 0, flags: int = 0) list[str][source]¶
The attribute/value pairs of one RDN, as text.
anyldap.ldap.filter module¶
ldap.filter: building search filters out of untrusted values.
- anyldap.ldap.filter.escape_filter_chars(assertion_value: str | bytes, escape_mode: int = 0) str[source]¶
An assertion value with the characters a filter would read escaped.
escape_mode0 escapes what RFC 4515 requires, 1 escapes everything outside the printable range as well, and 2 escapes every character.Each mode escapes what python-ldap’s own escapes, character by character as it does, so a filter built here is the one it would have built.
- anyldap.ldap.filter.filter_format(filter_template: str, assertion_values: Iterable[str | bytes]) str[source]¶
A filter built from a template, with every value escaped first.
- anyldap.ldap.filter.time_span_filter(filterstr: str = '', from_timestamp: float = 0, until_timestamp: float | None = None, delta_attr: str = 'modifyTimestamp') str[source]¶
This filter, narrowed to what changed between two times.
A negative
from_timestampis that many seconds before the end of the span, which is now unlessuntil_timestampsays otherwise.
anyldap.ldap.modlist module¶
ldap.modlist: turning entries into the modlists add and modify take.
- anyldap.ldap.modlist.addModlist(entry: Mapping[str, Sequence[str | bytes | None] | str | bytes | None], ignore_attr_types: Iterable[str] = ()) Sequence[tuple[str, Sequence[str | bytes] | str | bytes]][source]¶
The modlist that adds an entry, skipping attributes with no values.
- anyldap.ldap.modlist.modifyModlist(old_entry: Mapping[str, Sequence[str | bytes | None] | str | bytes | None], new_entry: Mapping[str, Sequence[str | bytes | None] | str | bytes | None], ignore_attr_types: Iterable[str] = (), ignore_oldexistent: int = 0, case_ignore_attr_types: Iterable[str] = ()) Sequence[tuple[int, str, Sequence[str | bytes] | str | bytes | None]][source]¶
The modlist that turns one entry into another.
An attribute whose values changed is deleted and added again rather than replaced, which is what python-ldap’s own does: the server then rejects the change if the entry moved underneath, instead of overwriting it.
anyldap.ldap.cidict module¶
ldap.cidict: a dictionary that ignores the case of its keys.
LDAP attribute types are case-insensitive, so an entry read back as
givenName answers to givenname too.
anyldap.ldap.functions module¶
ldap.functions: the module-level helpers python-ldap keeps here.
- anyldap.ldap.functions.escape_str(escape_func: Callable[[_Escapable], str], val: str, *args: _Escapable) str[source]¶
A template filled in with values that were escaped first.
escape_funcis what to escape them with:escape_filter_charsfor a filter,escape_dn_charsfor a DN.
- anyldap.ldap.functions.get_option(option: int) object[source]¶
What a connection opened from now on would start with.
- anyldap.ldap.functions.set_option(option: int, invalue: object) None[source]¶
Set an option on every connection opened from now on.
A connection that is already open keeps what it has; set the option on the connection itself to change that one.
- anyldap.ldap.functions.strf_secs(secs: float) str[source]¶
Seconds since the epoch, as the generalized time LDAP writes.
- anyldap.ldap.functions.strp_secs(dt_str: str) int[source]¶
A generalized time, as seconds since the epoch.
The time read is UTC, so it is turned back into seconds as UTC. python-ldap goes through
time.mktime()and subtractstime.timezone, which is an hour out wherever the local zone is on summer time; this is the inverse ofstrf_secs()everywhere.
anyldap.ldap.sasl module¶
ldap.sasl: the mechanisms a SASL bind can be made with.
A mechanism is an object that knows its name and answers the challenges the
server sends, which is the same shape python-ldap’s ldap.sasl classes
have. python-ldap hands the exchange to Cyrus SASL; the mechanisms here
answer for themselves, so what is available is what is written below.
- class anyldap.ldap.sasl.cram_md5(authc_id: str, password: str)[source]¶
Bases:
saslCRAM-MD5: the server’s challenge, answered with a keyed digest.
RFC 2195. The password is not sent, and the challenge is used once.
- class anyldap.ldap.sasl.digest_md5(authc_id: str, password: str, authz_id: str = '')[source]¶
Bases:
saslDIGEST-MD5: a challenge answered without sending the password.
RFC 2831, which is historic; a server that offers it usually offers something better as well.
- class anyldap.ldap.sasl.external(authz_id: str | None = None)[source]¶
Bases:
saslEXTERNAL: the identity is the one the connection already proved.
Which is TLS with a client certificate, or the credentials of whoever opened a socket in the filesystem.
- class anyldap.ldap.sasl.gssapi(authz_id: str = '', service: str | None = None)[source]¶
Bases:
saslGSSAPI: Kerberos, as RFC 4752 exchanges it.
The exchange needs a Kerberos library, which the
gssapipackage is: it is not a dependency, and this mechanism says so if it is asked for without it. The ticket comes from the caller’s credential cache, so there is nothing to pass but the identity to act as, if any.servicenames what the ticket is for. Left unset, the bind fills it in withldap@and the host it is connecting to, which is what a server registers itself as.
- class anyldap.ldap.sasl.plain(authc_id: str, password: str, authz_id: str = '')[source]¶
Bases:
saslPLAIN: the name and password, sent as they are (RFC 4616).
- class anyldap.ldap.sasl.sasl(cb_value_dict: Mapping[int, str] | None, mech: str | bytes)[source]¶
Bases:
objectA SASL mechanism, as python-ldap’s base class names one.
process()is given what the server sent, or nothing on the first step, and answers with what to send back; the bind is finished when the server says it is.
anyldap.ldap.controls package¶
ldap.controls: the controls a request carries and a response answers with.
A control is an OID, whether the server must understand it, and a value whose encoding the control itself knows. python-ldap encodes those values with pyasn1; here they are encoded with the BER library anyldap already has, so nothing else is needed to send or read one.
A request control is turned into the (type, criticality, value) triple
that goes on the wire by encode_controls(), and the triples a server
answers with are turned back into control objects by
decode_controls(), which knows the controls registered in
KNOWN_RESPONSE_CONTROLS.
- anyldap.ldap.controls.DecodeControlTuples(ldapControlTuples: Iterable[tuple[str | bytes, int | None, str | bytes | None]] | None, knownLDAPControls: Mapping[str, type[ResponseControl]] | None = None) list[ResponseControl]¶
The controls a response carried, read into the classes that know them.
A control whose OID is not registered comes back as an
LDAPControlholding the bytes the server sent, so nothing is lost by not knowing it.
- anyldap.ldap.controls.EncodeControlTuples(ldapControls: Iterable[RequestControl] | None) list[tuple[str | bytes, int | None, str | bytes | None]] | None¶
The triples that go on the wire for these controls.
- class anyldap.ldap.controls.LDAPControl(controlType: str | None = None, criticality: bool = False, controlValue: bytes | None = None, encodedControlValue: bytes | None = None)[source]¶
Bases:
RequestControl,ResponseControlA control that is only its three parts, whatever they mean.
- controlValue: bytes | None¶
- class anyldap.ldap.controls.RequestControl(controlType: str | None = None, criticality: bool = False, encodedControlValue: bytes | None = None)[source]¶
Bases:
objectA control sent with a request, as python-ldap’s base class is.
- controlType: str | None¶
- criticality: bool¶
- encodeControlValue() bytes | None[source]¶
The value as it goes on the wire, or None when it has none.
- encodedControlValue: bytes | None¶
- anyldap.ldap.controls.RequestControlTuples(ldapControls: Iterable[RequestControl] | None) list[tuple[str | bytes, int | None, str | bytes | None]] | None[source]¶
python-ldap’s name for what goes on the wire.
- class anyldap.ldap.controls.ResponseControl(controlType: str | None = None, criticality: bool = False)[source]¶
Bases:
objectA control a response carried, as python-ldap’s base class is.
- controlType: str | None¶
- criticality: bool¶
- decodeControlValue(encodedControlValue: bytes) None[source]¶
Read the value the server sent into this control’s own fields.
- encodedControlValue: bytes | None¶
- anyldap.ldap.controls.ResponseControlTuples(ldapControls: Iterable[ResponseControl] | None) Sequence[ResponseControl][source]¶
- anyldap.ldap.controls.decodeControlTuples(ldapControlTuples: Iterable[tuple[str | bytes, int | None, str | bytes | None]] | None, knownLDAPControls: Mapping[str, type[ResponseControl]] | None = None) list[ResponseControl]¶
The controls a response carried, read into the classes that know them.
A control whose OID is not registered comes back as an
LDAPControlholding the bytes the server sent, so nothing is lost by not knowing it.
- anyldap.ldap.controls.decode_controls(ldapControlTuples: Iterable[tuple[str | bytes, int | None, str | bytes | None]] | None, knownLDAPControls: Mapping[str, type[ResponseControl]] | None = None) list[ResponseControl][source]¶
The controls a response carried, read into the classes that know them.
A control whose OID is not registered comes back as an
LDAPControlholding the bytes the server sent, so nothing is lost by not knowing it.
- anyldap.ldap.controls.encodeControlTuples(ldapControls: Iterable[RequestControl] | None) list[tuple[str | bytes, int | None, str | bytes | None]] | None¶
The triples that go on the wire for these controls.
- anyldap.ldap.controls.encode_controls(ldapControls: Iterable[RequestControl] | None) list[tuple[str | bytes, int | None, str | bytes | None]] | None[source]¶
The triples that go on the wire for these controls.
ldap.controls.simple: the controls whose value is simple or absent.
- class anyldap.ldap.controls.simple.AuthorizationIdentityRequestControl(criticality: bool = False)[source]¶
Bases:
ValueLessRequestControlAsk the bind to say which identity it ended up as.
- class anyldap.ldap.controls.simple.AuthorizationIdentityResponseControl(controlType: str | None = '2.16.840.1.113730.3.4.15', criticality: bool = False)[source]¶
Bases:
ResponseControlThe identity the bind ended up as.
- class anyldap.ldap.controls.simple.BooleanControl(controlType: str | None = None, criticality: bool = False, booleanValue: bool = False)[source]¶
Bases:
RequestControl,ResponseControlA control whose value is yes or no.
- boolean2ber = {False: b'\x01\x01\x00', True: b'\x01\x01\xff'}¶
- class anyldap.ldap.controls.simple.GetEffectiveRightsControl(criticality: bool = False, authzId: str | None = None)[source]¶
Bases:
RequestControlAsk what this identity would be allowed to do with what is found.
- class anyldap.ldap.controls.simple.ManageDSAITControl(criticality: bool = False)[source]¶
Bases:
ValueLessRequestControlAsk for the entry itself rather than what it refers to.
- class anyldap.ldap.controls.simple.OctetStringInteger(controlType: str | None = None, criticality: bool = False, integerValue: int | None = None)[source]¶
Bases:
RequestControl,ResponseControlA control whose value is one integer, written as an octet string.
- class anyldap.ldap.controls.simple.ProxyAuthzControl(criticality: bool, authzId: str)[source]¶
Bases:
RequestControlAsk the server to act as somebody else, if it will let you.
- class anyldap.ldap.controls.simple.RelaxRulesControl(criticality: bool = False)[source]¶
Bases:
ValueLessRequestControlAsk the server to allow what its own rules would not.
- class anyldap.ldap.controls.simple.ValueLessRequestControl(controlType: str | None = None, criticality: bool = False)[source]¶
Bases:
RequestControlA control that says all it has to say by being there at all.
ldap.controls.pagedresults: reading a large search a page at a time.
RFC 2696. The control carries how many entries a page holds and, after the first page, the cookie the server gave for asking for the next one.
- class anyldap.ldap.controls.pagedresults.SimplePagedResultsControl(criticality: bool = False, size: int = 10, cookie: bytes = b'')[source]¶
Bases:
RequestControl,ResponseControlHow big a page is, and where the last one left off.
cookieis empty to ask for the first page, and is whatever the server last sent to ask for the one after it. An empty cookie coming back means there are no more pages.- controlType: str | None = '1.2.840.113556.1.4.319'¶
ldap.controls.readentry: the entry as it was, or as it became.
RFC 4527. A pre-read control asks for the entry the way it stood before the operation, a post-read for the way it stands after, so a caller does not have to search again to find out.
- class anyldap.ldap.controls.readentry.PostReadControl(criticality: bool = False, attrList: Sequence[str] | None = None)[source]¶
Bases:
ReadEntryControlThe entry as it stands after the operation.
- controlType: str | None = '1.3.6.1.1.13.2'¶
- class anyldap.ldap.controls.readentry.PreReadControl(criticality: bool = False, attrList: Sequence[str] | None = None)[source]¶
Bases:
ReadEntryControlThe entry as it stood before the operation.
- controlType: str | None = '1.3.6.1.1.13.1'¶
- class anyldap.ldap.controls.readentry.ReadEntryControl(criticality: bool = False, attrList: Sequence[str] | None = None)[source]¶
Bases:
RequestControl,ResponseControlThe attributes to read, and the entry that came back.
ldap.controls.sss: asking the server to sort what it sends back.
RFC 2891. The request names the attributes to sort by, each optionally with the matching rule to sort with and whether to reverse it; the response says whether the sort could be done.
- class anyldap.ldap.controls.sss.SSSRequestControl(criticality: bool = False, ordering_rules: Sequence[str] | str | None = None)[source]¶
Bases:
RequestControlSort by these attributes, in this order.
An ordering rule is written after the attribute and a colon, and a leading minus sign sorts that attribute the other way round:
SSSRequestControl(ordering_rules=['-uidNumber', 'cn:caseIgnoreMatch'])
- controlType: str | None = '1.2.840.113556.1.4.473'¶
- class anyldap.ldap.controls.sss.SSSResponseControl(criticality: bool = False)[source]¶
Bases:
ResponseControlWhether the sort was done, and what stopped it if it was not.
- controlType: str | None = '1.2.840.113556.1.4.474'¶
- decodeControlValue(encodedControlValue: bytes) None[source]¶
Read the value the server sent into this control’s own fields.
- sortResultCodes = {0: 'success', 1: 'operationsError', 3: 'timeLimitExceeded', 8: 'strongAuthRequired', 11: 'adminLimitExceeded', 16: 'noSuchAttribute', 18: 'inappropriateMatching', 50: 'insufficientAccessRights', 51: 'busy', 53: 'unwillingToPerform', 80: 'other'}¶
ldap.controls.ppolicy: what the server says about the password.
The password policy response control (draft-behera-ldap-password-policy): how long the password has left, how many grace logins remain, and what is wrong with it if the operation was refused because of it.
- class anyldap.ldap.controls.ppolicy.PasswordPolicyControl(criticality: bool = False)[source]¶
Bases:
ValueLessRequestControl,ResponseControlAsk about the password policy, and read what the server said.
- controlType: str | None = '1.3.6.1.4.1.42.2.27.8.5.1'¶
ldap.controls.libldap: the controls python-ldap encodes in its C library.
They are encoded here the same way everything else is, with the BER library anyldap already has, so the module is only a place for the names to live.
- class anyldap.ldap.controls.libldap.AssertionControl(criticality: bool = True, filterstr: str = '(objectClass=*)')[source]¶
Bases:
RequestControlDo the operation only if the entry matches this filter (RFC 4528).
- controlType: str | None = '1.3.6.1.1.12'¶
- class anyldap.ldap.controls.libldap.MatchedValuesControl(criticality: bool = False, filterstr: str = '(objectClass=*)')[source]¶
Bases:
RequestControlSend back only the values that match this filter (RFC 3876).
- controlType: str | None = '1.2.826.0.1.3344810.2.3'¶
ldap.controls.openldap: the controls OpenLDAP has of its own.
- class anyldap.ldap.controls.openldap.SearchNoOpControl(criticality: bool = False)[source]¶
Bases:
ValueLessRequestControl,ResponseControlAsk what a search would have found, without sending the entries.
The result carries the numbers instead: how many entries the search matched, and how many references it would have followed. See https://www.openldap.org/its/index.cgi?findid=6598.
- controlType: str | None = '1.3.6.1.4.1.4203.666.5.18'¶
- class anyldap.ldap.controls.openldap.SearchNoOpMixIn[source]¶
Bases:
objectWhat a connection mixes in to count entries without reading them.
python-ldap keeps
noop_search_sthere rather than on the connection itself, so that a caller who wants it says so:class Connection(SearchNoOpMixIn, ldap.SimpleLDAPObject): pass
anyldap.ldap.ldapurl module¶
ldapurl: LDAP URLs, as RFC 4516 writes them.
A URL says where a server is and what to ask it: the host and port, the DN
to start from, the attributes, scope and filter of a search, and any
extensions after that. python-ldap keeps this in a top-level ldapurl
module; the class and its parts are the same here.
- class anyldap.ldap.ldapurl.LDAPUrl(ldapUrl: str | None = None, urlscheme: str = 'ldap', hostport: str = '', dn: str = '', attrs: Sequence[str] | None = None, scope: int | None = None, filterstr: str | None = None, extensions: LDAPUrlExtensions | None = None, who: str | None = None, cred: str | None = None)[source]¶
Bases:
objectAn LDAP URL, taken apart into what it says.
Either
ldapUrlis given and read, or the parts are given one by one.attr2extypesays which attribute of the object an extension stands for, which is what lets a subclass carry the bind DN in the URL.- applyDefaults(defaults: Mapping[str, object]) None[source]¶
Fill in what the URL did not say from these defaults.
- attr2extype: dict[str, str] = {'cred': 'X-BINDPW', 'who': 'bindname'}¶
- class anyldap.ldap.ldapurl.LDAPUrlExtension(extensionStr: str | None = None, critical: int = 0, extype: str | None = None, exvalue: str | None = None)[source]¶
Bases:
objectOne extension of a URL: its type, its value, and whether it is critical.
An extension is written
type=value, with a leading exclamation mark when the server must understand it.
- class anyldap.ldap.ldapurl.LDAPUrlExtensions(default: Mapping[str, LDAPUrlExtension] | None = None)[source]¶
Bases:
MutableMapping[str,LDAPUrlExtension]The extensions of a URL, keyed by the type each one is of.
anyldap.ldap.ldif module¶
python-ldap’s ldif module: reading and writing LDIF (RFC 2849).
anyldap has an LDIF reader of its own in
anyldap.protocols.ldap.ldifprotocol, but it is a line-receiving
protocol that answers with the objects the rest of anyldap is built out of,
so it is not what code written against python-ldap asks for. This is that
module instead: the same classes, taking the same arguments and handing back
the same (dn, entry) pairs, so a script that reads or writes LDIF ports
without being rewritten.
Nothing here touches the network, so nothing here is awaited:
from anyldap.ldap import ldif
records = ldif.LDIFRecordList(open("people.ldif"))
records.parse()
for dn, entry in records.all_records:
print(dn, entry)
An attribute value is bytes and a DN is str, which is what
python-ldap makes of them too.
- class anyldap.ldap.ldif.LDIFCopy(input_file: _Input, output_file: _TextOutput, ignored_attr_types: Iterable[str] | None = None, max_entries: int = 0, process_url_schemes: Iterable[str] | None = None, base64_attrs: Iterable[str] | None = None, cols: int = 76, line_sep: str = '\n')[source]¶
Bases:
LDIFParserCopy LDIF input to LDIF output containing all data retrieved via URLs
- class anyldap.ldap.ldif.LDIFParser(input_file: _Input, ignored_attr_types: Iterable[str] | None = None, max_entries: int = 0, process_url_schemes: Iterable[str] | None = None, line_sep: str = '\n')[source]¶
Bases:
objectBase class for a LDIF parser. Applications should sub-class this class and override method handle() to implement something meaningful.
Public class attributes:
- records_read
Counter for records processed so far
- handle(dn: str, entry: dict[str, list[bytes | None]]) None[source]¶
Process a single content LDIF record. This method should be implemented by applications using LDIFParser.
- class anyldap.ldap.ldif.LDIFRecordList(input_file: _Input, ignored_attr_types: Iterable[str] | None = None, max_entries: int = 0, process_url_schemes: Iterable[str] | None = None)[source]¶
Bases:
LDIFParserCollect all records of a LDIF file. It can be a memory hog!
Records are stored in
all_recordsas a single list of 2-tuples (dn, entry), after callingparse().- all_records: list[tuple[str, dict[str, list[bytes | None]]]]¶
List storing parsed records.
- handle(dn: str, entry: dict[str, list[bytes | None]]) None[source]¶
Append a single record to the list of all records (
all_records).
- class anyldap.ldap.ldif.LDIFWriter(output_file: _TextOutput, base64_attrs: Iterable[str] | None = None, cols: int = 76, line_sep: str = '\n')[source]¶
Bases:
objectWrite LDIF entry or change records to file object Copy LDIF input to a file output object containing all data retrieved via URLs
- unparse(dn: str, record: Mapping[str, Sequence[bytes]] | Sequence[tuple[str, Sequence[bytes]]] | Sequence[tuple[int, str, Sequence[bytes] | None]]) None[source]¶
- dn
string-representation of distinguished name
- record
Either a dictionary holding the LDAP entry {attrtype:record} or a list with a modify list like for LDAPObject.modify().
anyldap.ldap.asyncsearch module¶
ldap.asyncsearch: a search read result by result rather than all at once.
A search that would fill memory if it were all asked for at once is read
here as it arrives: startSearch() sends the request, and
processResults() reads answers and hands each one to
_processSingleResult(), which a subclass overrides to do whatever it is
collecting or writing. The classes below are python-ldap’s, and are used the
same way; only the two methods that touch the connection are awaited.
The hooks – preProcessing(), afterFirstResult(),
postProcessing() and _processSingleResult() – are plain methods, as
they are in python-ldap: they are called while a result is being dispatched
and cannot await.
- class anyldap.ldap.asyncsearch.AsyncSearchHandler(l: SimpleLDAPObject)[source]¶
Bases:
objectA search whose results are handled one at a time.
lis the connection to search on.- async processResults(ignoreResultsNumber: int = 0, processResultsCount: int = 0, timeout: float = -1) int[source]¶
Read the results and hand each one on.
The first
ignoreResultsNumberare dropped, and at mostprocessResultsCountare handled after that, or all of them if it is zero. Answers 1 if there are results left unread, which is when the search has been abandoned, and 0 if it ran out.
- async startSearch(searchRoot: str, searchScope: int, filterStr: str, attrList: Sequence[str] | None = None, attrsOnly: int = 0, timeout: float = -1, sizelimit: int = 0, serverctrls: Iterable[controls.RequestControl | pureldap.Control] | None = None, clientctrls: Iterable[controls.RequestControl | pureldap.Control] | None = None) None[source]¶
Send the search, without waiting for what it answers.
The arguments are
search_ext()’s, in the order python-ldap’sstartSearch()takes them.
- class anyldap.ldap.asyncsearch.Dict(l: SimpleLDAPObject)[source]¶
Bases:
AsyncSearchHandlerEvery entry, keyed by its distinguished name.
- class anyldap.ldap.asyncsearch.FileWriter(l: SimpleLDAPObject, f: SupportsWrite[Any], headerStr: str | bytes = '', footerStr: str | bytes = '')[source]¶
Bases:
AsyncSearchHandlerResults written to a file as they arrive.
headerStrgoes in before the first one andfooterStrafter the last, which is what the two hooks are for.
- class anyldap.ldap.asyncsearch.IndexedDict(l: SimpleLDAPObject, indexed_attrs: Sequence[str] | None = None)[source]¶
Bases:
DictEvery entry, and an index of which names hold which values.
- class anyldap.ldap.asyncsearch.LDIFWriter(l: SimpleLDAPObject, writer_obj: IO[bytes], headerStr: bytes = b'', footerStr: bytes = b'')[source]¶
Bases:
FileWriterResults written out as LDIF.
python-ldap takes either a file or one of its own
ldif.LDIFWriterobjects here; this takes the file, and writes each entry withanyldap.protocols.ldap.ldif.asLDIF(), which is what the rest of anyldap writes LDIF with.
- class anyldap.ldap.asyncsearch.List(l: SimpleLDAPObject)[source]¶
Bases:
AsyncSearchHandlerEvery result, kept as it came.
Which looks pointless until only a certain part of a long search is wanted, which is what
processResults()’s arguments say.
anyldap.ldap.syncrepl module¶
ldap.syncrepl: keeping a copy of what a server holds (RFC 4533).
A syncrepl search asks the server to say not only what matches now but what changes afterwards. The server answers each entry with a Sync State control saying whether it is present, added, changed or gone, says where it has got to in Sync Info messages sent while the search runs, and finishes with a Sync Done control carrying the cookie to start again from.
SyncreplConsumer drives that: a connection mixes it in, overrides
the syncrepl_* methods to keep whatever copy it is keeping, and calls
syncrepl_search() and then syncrepl_poll() until it answers False:
class Consumer(SyncreplConsumer, ldap.SimpleLDAPObject):
def syncrepl_entry(self, dn, attrs, uuid):
self.entries[uuid] = (dn, attrs)
async with Consumer(uri) as connection:
await connection.simple_bind_s(who, cred)
msgid = await connection.syncrepl_search(base, ldap.SCOPE_SUBTREE)
while await connection.syncrepl_poll(msgid=msgid):
pass
Only syncrepl_search() and syncrepl_poll() touch the connection and
are awaited; the methods a subclass overrides stay plain, as they are in
python-ldap, because they are called while a message is being dispatched.
- class anyldap.ldap.syncrepl.SyncDoneControl(criticality: bool = False)[source]¶
Bases:
ResponseControlThe end of a syncrepl search: where it got to, and what that means.
refreshDeletessays whether the server named the entries that are gone, or whether whatever was not sent is what is gone.- controlType: str | None = '1.3.6.1.4.1.4203.1.9.1.3'¶
- class anyldap.ldap.syncrepl.SyncInfoMessage(encodedMessage: bytes)[source]¶
Bases:
objectWhat the server says while a syncrepl search is running.
One of four things: a new cookie on its own, the end of the refresh phase with the entries that are gone named or not named, or a set of entries that are all present or all gone.
- responseName = '1.3.6.1.4.1.4203.1.9.1.4'¶
- class anyldap.ldap.syncrepl.SyncRequestControl(criticality: bool = True, cookie: str | bytes | None = None, mode: str = 'refreshOnly', reloadHint: bool = False)[source]¶
Bases:
RequestControlAsk a search to be a syncrepl search.
modeisrefreshOnlyto be told what there is now and stop, orrefreshAndPersistto be told about changes as they happen.cookieis where a previous search got to, and starting from it asks for what has changed since.- controlType: str | None = '1.3.6.1.4.1.4203.1.9.1.1'¶
- class anyldap.ldap.syncrepl.SyncStateControl(criticality: bool = False)[source]¶
Bases:
ResponseControlWhat has become of the entry the server just sent.
It is present as it was, newly added, changed, or gone; the entry’s UUID is what says which entry it is from one message to the next.
- controlType: str | None = '1.3.6.1.4.1.4203.1.9.1.2'¶
- decodeControlValue(encodedControlValue: bytes) None[source]¶
Read the value the server sent into this control’s own fields.
- opnames = ('present', 'add', 'modify', 'delete')¶
- class anyldap.ldap.syncrepl.SyncreplConsumer(*args: Any, **kwargs: Any)[source]¶
Bases:
objectA connection that keeps a copy of what the server holds.
Only one syncrepl search can be running on one of these at a time; two at once need two connections, as they do in python-ldap.
- syncrepl_entry(dn: str, attrs: dict[str, list[bytes]], uuid: str) None[source]¶
This entry was added or changed, and this is what it is now.
- syncrepl_get_cookie() str | bytes | None[source]¶
The cookie that was stored, to start the next search from.
- async syncrepl_poll(msgid: int = -1, timeout: float | None = None, all: int = 0) bool[source]¶
Read what the search has said, and hand it on.
Answers False once the search is finished and True while it is still running. With
allset it reads until the search finishes rather than returning after one message.
- syncrepl_present(uuids: Sequence[str] | None, refreshDeletes: bool | None = False) None[source]¶
These entries are still there, or the refresh has finished.
Given a list of UUIDs, record them as present. Given None and
refreshDeletesfalse, delete everything that was not recorded and start the record again; given None andrefreshDeletestrue, start the record again without deleting anything.
- async syncrepl_search(base: str, scope: int = 2, mode: str = 'refreshOnly', cookie: str | bytes | None = None, **search_args: Any) int[source]¶
Start the search, and answer with the message id it was sent as.
base,scopeand the rest aresearch_ext()’s, with the Sync Request control added to whatever controls were given. The cookie is asked ofsyncrepl_get_cookie()if it is not passed.
anyldap.ldap.extop package¶
ldap.extop: the extended operations a server may offer.
An extended operation is a request named by an OID with a value whose shape
that OID decides, and a response of the same shape. These classes are what a
caller builds one out of: a subclass says what its OID is, writes its request
value out and reads its response value back, and
extop_s() sends it.
- class anyldap.ldap.extop.ExtendedRequest(requestName: str, requestValue: bytes)[source]¶
Bases:
objectAn extended operation being asked for.
requestNameis the OID that says which operation it is, andrequestValuethe bytes that go with it. A subclass says which OID it is by naming one here, which is what the connection asks it for.- requestName: str | None = None¶
- class anyldap.ldap.extop.ExtendedResponse(responseName: str | None, encodedResponseValue: bytes)[source]¶
Bases:
objectWhat the server answered an extended operation with.
A subclass names the OID it reads, which is what
extop_s()checks the server answered to before handing the answer over.- responseName: str | None = None¶
- class anyldap.ldap.extop.PasswordModifyResponse(responseName: str | None, encodedResponseValue: bytes)[source]¶
Bases:
ExtendedResponseThe password the server made up, when it was not given one.
- responseName: str | None = None¶
- class anyldap.ldap.extop.RefreshRequest(requestName: str | None = None, entryName: str | None = None, requestTtl: int | None = None)[source]¶
Bases:
ExtendedRequestAsk that a dynamic entry go on living for another
requestTtl.- defaultRequestTtl = 86400¶
- requestName: str | None = '1.3.6.1.4.1.1466.101.119.1'¶
- class anyldap.ldap.extop.RefreshResponse(responseName: str | None, encodedResponseValue: bytes)[source]¶
Bases:
ExtendedResponseHow long the server says the entry has left.
- responseName: str | None = '1.3.6.1.4.1.1466.101.119.1'¶
ldap.extop.dds: entries that go away by themselves (RFC 2589).
A dynamic entry lives for as long as its time to live says, and a refresh request asks the server to start that clock again.
- class anyldap.ldap.extop.dds.RefreshRequest(requestName: str | None = None, entryName: str | None = None, requestTtl: int | None = None)[source]¶
Bases:
ExtendedRequestAsk that a dynamic entry go on living for another
requestTtl.- defaultRequestTtl = 86400¶
- requestName: str | None = '1.3.6.1.4.1.1466.101.119.1'¶
- class anyldap.ldap.extop.dds.RefreshResponse(responseName: str | None, encodedResponseValue: bytes)[source]¶
Bases:
ExtendedResponseHow long the server says the entry has left.
- responseName: str | None = '1.3.6.1.4.1.1466.101.119.1'¶
ldap.extop.passwd: the password modify operation (RFC 3062).
- class anyldap.ldap.extop.passwd.PasswordModifyResponse(responseName: str | None, encodedResponseValue: bytes)[source]¶
Bases:
ExtendedResponseThe password the server made up, when it was not given one.
- responseName: str | None = None¶
anyldap.ldap.schema package¶
ldap.schema: the schema a server publishes, read into objects.
ldap.schema.models: what a server says its schema is made of.
Each class reads one schema definition – the text a server publishes in its
subschema subentry – and answers with the fields python-ldap’s own model
classes answer with. The reading itself is anyldap.schema, which the
rest of anyldap already parses schema with.
- class anyldap.ldap.schema.models.AttributeType(schema_element_str: str | bytes | None = None)[source]¶
Bases:
SchemaElementAn attribute type: how its values are written and compared.
- schema_attribute: ClassVar[str] = 'attributeTypes'¶
- class anyldap.ldap.schema.models.DITContentRule(schema_element_str: str | bytes | None = None)[source]¶
Bases:
_TokenisedElementWhat an entry of a structural class may hold on top of it.
- schema_attribute: ClassVar[str] = 'dITContentRules'¶
- token_defaults: ClassVar[dict[str, tuple[str | None, ...] | None]] = {'AUX': (), 'DESC': (None,), 'MAY': (), 'MUST': (), 'NAME': (), 'NOT': (), 'OBSOLETE': None}¶
- class anyldap.ldap.schema.models.DITStructureRule(schema_element_str: str | bytes | None = None)[source]¶
Bases:
_TokenisedElementWhere in the tree an entry of a name form may be put.
A structure rule is numbered rather than named by an OID, which is what
ruleidis;get_id()answers with it.- schema_attribute: ClassVar[str] = 'dITStructureRules'¶
- token_defaults: ClassVar[dict[str, tuple[str | None, ...] | None]] = {'DESC': (None,), 'FORM': (None,), 'NAME': (), 'OBSOLETE': None, 'SUP': ()}¶
- class anyldap.ldap.schema.models.Entry(schema: subentry.SubSchema, dn: str, entry: Mapping[str, list[bytes]])[source]¶
Bases:
UserDict[Any,list[bytes]]An entry that knows the schema its attributes are described by.
An attribute is looked up by whichever of its names or its OID the caller has, so
entry["cn"],entry["commonName"]andentry["2.5.4.3"]are all the same attribute; sub-types are kept apart, socn;lang-enis notcn.- attribute_types(attr_type_filter: object = None, raise_keyerror: int = 1) tuple[dict[str, AttributeType], dict[str, AttributeType]][source]¶
What an entry of this one’s object classes must and may have.
- class anyldap.ldap.schema.models.LDAPSyntax(schema_element_str: str | bytes | None = None)[source]¶
Bases:
SchemaElementA syntax: what a value of it looks like.
- schema_attribute: ClassVar[str] = 'ldapSyntaxes'¶
- class anyldap.ldap.schema.models.MatchingRule(schema_element_str: str | bytes | None = None)[source]¶
Bases:
SchemaElementA matching rule: how two values of a syntax are compared.
- schema_attribute: ClassVar[str] = 'matchingRules'¶
- class anyldap.ldap.schema.models.MatchingRuleUse(schema_element_str: str | bytes | None = None)[source]¶
Bases:
_TokenisedElementWhich attributes a matching rule may be used with.
- schema_attribute: ClassVar[str] = 'matchingRuleUse'¶
- token_defaults: ClassVar[dict[str, tuple[str | None, ...] | None]] = {'APPLIES': (), 'DESC': (None,), 'NAME': (), 'OBSOLETE': None}¶
- class anyldap.ldap.schema.models.NameForm(schema_element_str: str | bytes | None = None)[source]¶
Bases:
_TokenisedElementWhich attributes name an entry of an object class.
- schema_attribute: ClassVar[str] = 'nameForms'¶
- token_defaults: ClassVar[dict[str, tuple[str | None, ...] | None]] = {'DESC': (None,), 'MAY': (), 'MUST': (), 'NAME': (), 'OBSOLETE': None, 'OC': (None,)}¶
- class anyldap.ldap.schema.models.ObjectClass(schema_element_str: str | bytes | None = None)[source]¶
Bases:
SchemaElementAn object class: what an entry of it must and may have.
- schema_attribute: ClassVar[str] = 'objectClasses'¶
- class anyldap.ldap.schema.models.SchemaElement(schema_element_str: str | bytes | None = None)[source]¶
Bases:
objectOne definition out of a schema, whatever kind it is.
- key_attr(key: str, value: str | None, quoted: int = 0) str[source]¶
One field of a definition, written the way python-ldap writes it.
- key_list(key: str, values: tuple[str, ...], sep: str = ' ', quoted: int = 0) str[source]¶
A field with several values, written the way python-ldap writes it.
- schema_attribute: ClassVar[str] = ''¶
ldap.schema.subentry: the schema a server publishes, read into objects.
- exception anyldap.ldap.schema.subentry.NameNotUnique(desc: str)[source]¶
Bases:
SubschemaErrorTwo definitions of the same kind claim one name.
- exception anyldap.ldap.schema.subentry.OIDNotUnique(desc: str)[source]¶
Bases:
SubschemaErrorTwo definitions of the same kind claim one OID.
- class anyldap.ldap.schema.subentry.SubSchema(sub_schema_sub_entry: Mapping[str, Iterable[bytes | str]], check_uniqueness: int = 1)[source]¶
Bases:
objectThe schema of one server, looked up by name or by OID.
Built from the entry a server publishes at its subschema subentry: the dictionary of attributes a search of it hands back.
- attribute_types(object_class_list: Sequence[str], attr_type_filter: object = None, raise_keyerror: int = 1, ignore_dit_content_rule: int = 0) tuple[dict[str, AttributeType], dict[str, AttributeType]][source]¶
What entries of these object classes must and may have.
Two dictionaries of attribute types by OID: the ones that are required, and the ones that are allowed.
- get_obj(se_class: type[SchemaElement], nameoroid: str, default: SchemaElement | None = None, raise_keyerror: int = 0) SchemaElement | None[source]¶
What the server said about this name, or about this OID.
- get_structural_oc(oc_list: Sequence[str]) str | None[source]¶
The one object class of these that says what the entry is.
- getoid(se_class: type[SchemaElement], nameoroid: str, raise_keyerror: int = 0) str[source]¶
The OID a name stands for, or the OID itself.
Sub-types are dropped first, so
cn;lang-enis asked about ascn. A name the schema does not describe comes back as it was given unlessraise_keyerrorsays to make something of it.
- ldap_entry() dict[str, list[str]][source]¶
The entry this schema was read from, written out again.
Each element as the definition it would be published as, keyed by the attribute it is published in.
- listall(schema_element_class: type[SchemaElement], schema_element_filters: object = None) list[str][source]¶
The OIDs of everything of this kind that the server published.
- exception anyldap.ldap.schema.subentry.SubschemaError[source]¶
Bases:
ValueErrorSomething is wrong with the schema a server published.
- async anyldap.ldap.schema.subentry.urlfetch(uri: str, trace_level: int = 0) tuple[str | None, SubSchema | None][source]¶
The schema an LDAP URL, or an LDIF file, says a server publishes.
An
ldap://,ldaps://orldapi://URL is asked: a connection is opened, bound as the URL says to bind, asked where its schema is and then for the schema itself, and closed again – which is what makes this different fromread_schema_s()on a connection that is already open. Anything else is the address of an LDIF file, and its first record is the schema, which is what python-ldap makes of one too. Unlike python-ldap’s, which hands the address tourlopen, onlyfile:,http:andhttps:are read.
ldap.schema.tokenizer: a schema definition, taken apart word by word.
A schema definition is parentheses, bare words and quoted strings, and what
each word means depends only on the keyword before it. That is enough to
read the kinds of definition that have no parser of their own in
anyldap.schema – content rules, structure rules, name forms and
matching rule uses – and it is how python-ldap reads every kind.
- anyldap.ldap.schema.tokenizer.extract_tokens(l: list[str], known_tokens: dict[str, tuple[str | None, ...] | None]) dict[str, tuple[str | None, ...] | None][source]¶
What each keyword was given, for the keywords asked about.
known_tokenssays which keywords to look for and what each one is worth when the definition does not mention it.