### Install Bonsai using Pip Source: https://github.com/noirello/bonsai/blob/master/docs/install.md The simplest way to install Bonsai is by using pip. This command installs the latest stable version from the Python Package Index. ```bash pip install bonsai ``` -------------------------------- ### Compile and Install Bonsai from Source on Windows Source: https://github.com/noirello/bonsai/blob/master/docs/install.md Compile and install Bonsai from source on Windows using a C compiler and the WinLDAP library. This method is suitable for Windows environments. ```bash python setup.py build python setup.py install ``` -------------------------------- ### Compile and Install Bonsai from Source on Linux Source: https://github.com/noirello/bonsai/blob/master/docs/install.md Compile and install Bonsai from source on Linux. Ensure you have the necessary development headers for Python, OpenLDAP, and SASL installed. ```bash python3 setup.py build sudo python3 setup.py install ``` -------------------------------- ### Install OpenLDAP on macOS Source: https://github.com/noirello/bonsai/blob/master/docs/install.md Install the openldap homebrew-core formula on macOS. This is a prerequisite for compiling Bonsai from source on macOS due to system library limitations. ```bash brew install openldap ``` -------------------------------- ### Connect to LDAP Server Source: https://github.com/noirello/bonsai/blob/master/docs/tutorial.md Establish a connection to an LDAP server using the LDAPClient. This is the basic setup for communicating with the server. ```python >>> from bonsai import LDAPClient >>> client = LDAPClient("ldap://example.org") >>> conn = client.connect() ``` -------------------------------- ### Async Search and Modify with asyncio Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Example demonstrating asynchronous search and modify operations using Python's asyncio library. Ensure the correct event loop policy is set for Windows compatibility. ```python import asyncio import sys import bonsai if sys.platform == 'win32': # The current asyncio connection implementation requires # the old selector event loop when running on Windows. asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) async def do(): cli = bonsai.LDAPClient("ldap://localhost") async with cli.connect(is_async=True) as conn: results = await conn.search("ou=nerdherd,dc=bonsai,dc=test", 1) for res in results: print(res['givenName'][0]) search = await conn.search("cn=chuck,ou=nerdherd,dc=bonsai,dc=test", 0) entry = search[0] entry['mail'] = "chuck@nerdherd.com" await entry.modify() asyncio.run(do()) ``` -------------------------------- ### Acquire Kerberos Ticket Granting Ticket (TGT) Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Use the kinit command-line tool to acquire a TGT for GSSAPI authentication. Ensure Kerberos tools and libraries are installed and configured. ```default [noirello@bonsai.test ~]$ kinit admin@BONSAI.TEST Password for admin@BONSAI.TEST: ``` -------------------------------- ### Handling Connection Timeouts with Connection Pools Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md This example shows a pattern for handling potential connection timeouts when using a pool. It closes a connection on failure and retries, ensuring a fresh connection if timeouts occur. The loop runs one more time than the number of idle connections. ```python3 def work(pool, base, scope, filter_exp, attrlist): idle_count = pool.idle_connection for try in range(idle_count + 1): with pool.spawn() as conn: try: result = conn.search( base, scope, filter_exp, attrlist, timeout=10 ) # Use the results somehow.... except bonsai.ConnectionError as e: conn.close() # If all of the idle connections have been tried and the # call still failed, there is something deeper wrong. if try == idle_count: raise ``` -------------------------------- ### Using Gevent with Bonsai LDAP Connections Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md This snippet shows how to configure the Bonsai LDAP client to use gevent for asynchronous operations by setting a custom connection class. It performs similar search and modify actions as the asyncio example. ```python import gevent import bonsai from bonsai.gevent import GeventLDAPConnection def do(): cli = bonsai.LDAPClient("ldap://localhost") # Change the default async conn class. cli.set_async_connection_class(GeventLDAPConnection) with cli.connect(True) as conn: results = conn.search("ou=nerdherd,dc=bonsai,dc=test", 1) for res in results: print(res['givenName'][0]) search = conn.search("cn=chuck,ou=nerdherd,dc=bonsai,dc=test", 0) entry = search[0] entry['mail'] = "chuck@nerdherd.com" entry.modify() gevent.joinall([gevent.spawn(do)]) ``` -------------------------------- ### Get LDAP Library Vendor and Version Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Fetches the vendor name and version number of the LDAP library. Returns a tuple containing the vendor name (string) and version (integer). ```python import bonsai print(bonsai.get_vendor_info()) ``` -------------------------------- ### Create Custom Async LDAP Connection Class Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Inherit from BaseLDAPConnection to create a custom asynchronous LDAP connection class. This example shows the basic structure for integrating with Curio. ```python from typing import Optional import bonsai import curio from bonsai.ldapconnection import BaseLDAPConnection # You have to inherit from BaseLDAPConnection. class CurioLDAPConnection(BaseLDAPConnection): def __init__(self, client: "LDAPClient"): super().__init__(client, is_async=True) async def _evaluate(self, msg_id: int, timeout: Optional[float] = None): while True: res = self.get_result(msg_id) if res is not None: return res await curio.sleep(1) ``` -------------------------------- ### Perform a One-Level Search Source: https://github.com/noirello/bonsai/blob/master/docs/tutorial.md Executes a search starting from a specified base DN, looking only one level down in the directory tree. It uses the LDAPSearchScope.ONE enumeration for clarity. ```python >>> conn = client.connect() >>> conn.search("ou=nerdherd,dc=bonsai,dc=test", bonsai.LDAPSearchScope.ONE, "(objectclass=*)") [{'dn': , 'sn': ['Bartowski'], 'cn': ['chuck'], 'givenName': ['Chuck'], 'objectClass': ['inetOrgPerson', 'organizationalPerson', 'person', 'top']}, {'dn': , 'sn': ['Patel'], cn': ['lester'], 'givenName': ['Laster'], 'objectClass': ['inetOrgPerson', 'organizationalPerson', 'person', 'top']}, {'dn': , 'sn': ['Barnes'], 'cn': ['jeff'], 'givenName': ['Jeff'], 'objectClass': ['inetOrgPerson', 'organizationalPerson', 'person', 'top']}] ``` -------------------------------- ### Set Extended DN Control Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Configures the client to receive extended DN format including GUID and SID attributes during LDAP searches. The parameter determines the string format of the GUID and SID. ```pycon >>> client = bonsai.LDAPClient() >>> client.set_extended_dn(1) >>> result = conn.search("ou=nerdherd,dc=bonsai,dc=test", 1) >>> result[0].extended_dn ;;cn=chuck,ou=nerdherd,dc=bonsai,dc=test >>> result[0].dn ``` -------------------------------- ### Initialize LDAPClient and perform a search Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Demonstrates how to initialize an LDAPClient with a base DN and search parameters, then connect and perform a search operation. ```python >>> from bonsai import LDAPClient >>> client = LDAPClient("ldap://localhost") # without additional parameters >>> conn = client.connect() >>> conn.search("ou=nerdherd,dc=bonsai,dc=test", 1, "(cn=ch*)", ["cn", "sn", "gn"]) [{'dn': , 'sn': ['Bartowski'], 'cn': ['chuck'], 'givenName': ['Chuck']}] ``` -------------------------------- ### Configure setup.cfg for Bonsai on macOS Source: https://github.com/noirello/bonsai/blob/master/docs/install.md Modify the setup.cfg file to specify include and library directories for OpenLDAP on macOS. This is necessary when compiling from source. ```ini [build_ext] include_dirs=/usr/local/opt/openldap/include library_dirs=/usr/local/opt/openldap/lib ``` ```ini [build_ext] library_dirs = /opt/homebrew/opt/openldap/lib include_dirs = /usr/include/sasl:/opt/homebrew/opt/openldap/include ``` -------------------------------- ### Initialize LDAPClient with URL parameters and search Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Shows how to initialize an LDAPClient using an LDAP URL that includes base DN, attributes, scope, and filter, then perform a search. ```python >>> client = LDAPClient("ldap://localhost/ou=nerdherd,dc=bonsai,dc=test?cn,sn,gn?one?(cn=ch*)") # with additional parameters >>> conn = client.connect() >>> conn.search() [{'dn': , 'sn': ['Bartowski'], 'cn': ['chuck'], 'givenName': ['Chuck']}] ``` -------------------------------- ### Connect and Use LDAP with Context Manager Source: https://github.com/noirello/bonsai/blob/master/docs/tutorial.md Demonstrates connecting to an LDAP server and performing operations within a context manager, which automatically handles closing the connection. ```python import bonsai cli = bonsai.LDAPClient("ldap://localhost") with cli.connect() as conn: res = conn.search("ou=nerdherd,dc=bonsai,dc=test", 1) print(res) print(conn.whoami()) ``` -------------------------------- ### Working with LDAPDN Objects Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Demonstrates creating, manipulating, and comparing LDAPDN objects. Shows how to access RDNs, convert to string, and modify parts of the DN. ```pycon >>> import bonsai >>> dn = bonsai.LDAPDN("cn=testuser,dc=bonsai,dc=test") >>> dn >>> dn.rdns # Get RDNs in tuple format. ((('cn', 'testuser'),), (('dc', 'bonsai'),), (('dc', 'test'),)) >>> str(dn) # Convert to string. 'cn=testuser,dc=bonsai,dc=test' >>> dn[1] # Get the second RDN. 'dc=bonsai' >>> dn[0] # Get the first RDN. 'cn=testuser' >>> dn[1] = "ou=nerdherd,dc=bonsai" # Change the second RDN. >>> dn >>> other_dn = bonsai.LDAPDN("cn=testuser,ou=nerdherd,dc=bonsai,dc=test") >>> dn == other_dn True >>> dn[1:3] # Get the second and third RDN. 'ou=nerdherd,dc=bonsai' >>> dn[1:3] = 'ou=buymore,dc=bonsai' # Change them. >>> dn ``` -------------------------------- ### Get TLS Implementation Name Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Retrieves the name of the underlying TLS implementation used by the LDAP library. Possible values include GnuTLS, OpenSSL, MozNSS, and SChannel. ```python import bonsai print(bonsai.get_tls_impl_name()) ``` -------------------------------- ### bonsai.has_krb5_support() Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Checks if the module was built with support for Kerberos/GSSAPI. ```APIDOC ## bonsai.has_krb5_support() * **Returns:** True if the module is built with the optional Kerberos/GSSAPI headers. * **Return type:** bool ``` -------------------------------- ### Manage LDAP Connections with ConnectionPool Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Shows how to create and use a ConnectionPool for managing LDAP connections. The 'spawn' method provides a connection within a context manager. ```python import bonsai from bonsai.pool import ConnectionPool client = bonsai.LDAPClient() pool = ConnectionPool(client) with pool.spawn() as conn: print(conn.whoami()) ``` -------------------------------- ### Asynchronous LDAP Search with asyncio Source: https://github.com/noirello/bonsai/blob/master/README.rst Shows how to use the Bonsai module with Python's asyncio library for asynchronous LDAP operations. This includes connecting, searching, and retrieving the 'whoami' information. ```python3 import asyncio import bonsai async def do(): client = bonsai.LDAPClient("ldap://localhost") client.set_credentials("DIGEST-MD5", user="admin", password="secret") async with client.connect(is_async=True) as conn: res = await conn.search("ou=nerdherd,dc=bonsai,dc=test", 2) print(res) who = await conn.whoami() print(who) asyncio.run(do()) ``` -------------------------------- ### Create and Add a New LDAP Entry Source: https://github.com/noirello/bonsai/blob/master/docs/tutorial.md Demonstrates creating an `LDAPEntry` object, setting its attributes including `objectClass` and `sn`, and adding it to an LDAP connection. Ensure all schema-required attributes are set to avoid `ObjectClassViolation`. ```pycon >>> from bonsai import LDAPEntry >>> anna = LDAPEntry("cn=anna,ou=nerdherd,dc=bonsai,dc=test") >>> anna['objectClass'] = ['top', 'inetOrgPerson'] # Must set schemas to get a valid LDAP entry. >>> anna['sn'] = "Wu" # Must set a surname attribute because inetOrgPerson schema requires. >>> anna['mail'] = "anna@nerdherd.com" >>> anna.dn >>> anna {'dn': , 'objectClass': ['top', 'inetOrgPerson'], 'sn': ['Wu'], 'mail': ['anna@nerdherd.com']} ``` ```pycon >>> conn.add(anna) True ``` -------------------------------- ### Simple Bind Authentication Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Use Simple Bind for basic authentication with a DN and password. Be cautious as passwords are sent in clear text without TLS/SSL. ```default >>> import bonsai >>> client = bonsai.LDAPClient() >>> client.set_credentials("SIMPLE", user="cn=user,dc=bonsai,dc=test", password="secret") >>> client.connect() ``` -------------------------------- ### Creating and Accessing LDAPEntry DN Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Shows how to create an LDAPEntry object and access its distinguished name (dn) attribute, including converting it to a string. ```pycon >>> from bonsai import LDAPEntry >>> anna = LDAPEntry('cn=anna,ou=nerdherd,dc=bonsai,dc=test') >>> anna.dn >>> str(anna.dn) 'cn=anna,ou=nerdherd,dc=bonsai,dc=test' ``` -------------------------------- ### Configure TLS and Authenticate with EXTERNAL Mechanism Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Set up TLS connection and client certificates, then authenticate using the EXTERNAL SASL mechanism. This is typically used with OpenLDAP when a TLS connection is established. ```python client = bonsai.LDAPClient("ldap://bonsai.test", tls=True) client.set_ca_cert_dir('/etc/openldap/certs') client.set_ca_cert("RootCACert") client.set_client_cert("BonsaiTestUser") client.set_client_key("./key.txt") client.get_rootDSE()['supportedSASLMechanisms'] ['GSS-SPNEGO', 'GSSAPI', 'DIGEST-MD5', 'EXTERNAL', 'NTLM'] client.set_credentials("EXTERNAL") client.connect() ``` -------------------------------- ### Authenticate with GSSAPI using Keytab Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Authenticate using GSSAPI with a Kerberos keytab file. This requires the module to be built with Kerberos support. ```python client.set_credentials("GSSAPI", user="chuck", realm="BONSAI.TEST", keytab="./user.keytab") client.connect().whoami() 'dn:cn=chuck,ou=nerdherd,dc=bonsai,dc=test' ``` -------------------------------- ### Connect to LDAP Server with TLS Parameter Source: https://github.com/noirello/bonsai/blob/master/docs/tutorial.md Alternatively, enable secure TLS connections by setting the 'tls' parameter to True when initializing LDAPClient. This is an alternative to using the 'ldaps://' scheme. ```python >>> client = LDAPClient("ldap://example.org", True) >>> conn = client.connect() ``` -------------------------------- ### Request TGT with Credentials for GSSAPI Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md On Windows systems or when Bonsai is built with optional Kerberos headers, request a TGT by providing username, password, and realm name. ```python client = bonsai.LDAPClient("ldap://bonsai.test") client.set_credentials("GSSAPI", "admin", "secret", "BONSAI.TEST") client.connect().whoami() 'dn:cn=admin,dc=bonsai,dc=test' ``` -------------------------------- ### Check for Kerberos/GSSAPI Support Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Determines if the Bonsai module was built with support for Kerberos/GSSAPI. Returns a boolean value. ```python import bonsai print(bonsai.has_krb5_support()) ``` -------------------------------- ### bonsai.get_vendor_info() Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Retrieves the vendor name and version number of the LDAP library. ```APIDOC ## bonsai.get_vendor_info() Return the vendor’s name and the version number of the LDAP library: ```pycon >>> bonsai.get_vendor_info() ("OpenLDAP", 20440) ``` * **Returns:** A tuple of the vendor’s name and the library’s version. * **Return type:** tuple ``` -------------------------------- ### Connect to LDAP Server via LDAPS Source: https://github.com/noirello/bonsai/blob/master/docs/tutorial.md Use the 'ldaps://' scheme for a secure connection over SSL/TLS. Ensure your server is configured for LDAPS. ```python >>> client = LDAPClient("ldaps://example.org") ``` -------------------------------- ### Perform a search with a different filter Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Illustrates performing a subsequent search on an existing connection with a different filter expression. ```python >>> conn.search(filter_exp="(cn=j*)") [{'dn': , 'sn': ['Barnes'], 'cn': ['jeff'], 'givenName': ['Jeff']}] ``` -------------------------------- ### Connect via File Socket Source: https://github.com/noirello/bonsai/blob/master/docs/tutorial.md Connect to a local LDAP server using a file socket by pointing the URL to 'ldapi://'. The file path must be URL-encoded. ```python >>> client = LDAPClient("ldapi://%2Frun%2Fslapd%2Fldapi") ``` -------------------------------- ### Using ThreadedConnectionPool for LDAP Operations Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md This snippet demonstrates how to use Bonsai's ThreadedConnectionPool to manage and reuse LDAP connections across different threads. It shows acquiring a connection, performing an operation, and returning the connection to the pool. ```python3 import bonsai import threading from bonsai.pool import ThreadedConnectionPool def work(pool): with pool.spawn() as conn: print(conn.whoami()) # Some other operations... client = bonsai.LDAPClient() pool = ThreadedConnectionPool(client, minconn=5, maxconn=10) thr = threading.Thread(target=work, args=(pool,)) thr.start() conn = pool.get() res = conn.search() # After finishing up... pool.put(conn) ``` -------------------------------- ### Read LDIF File Entries Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Demonstrates how to read entries from an LDIF file using LDIFReader. Ensure the file is opened in text mode. ```python import bonsai with open("./users.ldif") as fileobj: reader = bonsai.LDIFReader(fileobj) for entry in reader: print(entry.dn) ``` -------------------------------- ### Connect with User Credentials Source: https://github.com/noirello/bonsai/blob/master/docs/tutorial.md Authenticate to the LDAP server with specific user credentials using the 'set_credentials' method before connecting. This allows for authenticated operations. ```python >>> client = LDAPClient("ldaps://example.org") >>> client.set_credentials("SIMPLE", user="cn=test,dc=bonsai,dc=test", password="secret") >>> conn = client.connect() >>> conn.whoami() 'cn=test,dc=bonsai,dc=test' ``` -------------------------------- ### Test Custom Async Class with Curio Coroutines Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md A litmus test demonstrating the integration of the custom CurioLDAPConnection class with Curio's coroutine management. It spawns multiple tasks, including a countdown and an LDAP search, to verify concurrent execution. ```python async def countdown(n): while n > 0: print(f"T-minus {n}") await curio.sleep(1) n -= 1 async def search(): cli = bonsai.LDAPClient() cli.set_async_connection_class(CurioLDAPConnection) conn = await cli.connect(is_async=True) res = await conn.search("ou=nerdherd,dc=bonsai,dc=test", 1) for ent in res: print(ent.dn) async def tasks(): tsk1 = await curio.spawn(countdown, 20) tsk2 = await curio.spawn(search) await tsk1.join() await tsk2.join() if __name__ == "__main__": curio.run(tasks) ``` -------------------------------- ### bonsai.set_connect_async(allow) Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Enables or disables asynchronous connection for the underlying socket. This is an OpenLDAP specific setting. ```APIDOC ## bonsai.set_connect_async(allow) Disable/enable asynchronous connection for the underlying socket, which means that the socket is set to be non-blocking when it’s enabled. The default setting is False on every platform. This is an OpenLDAP specific setting (see LDAP_OPT_CONNECT_ASYNC option in the OpenLDAP documentation for further details). * **Parameters:** **allow** (*bool*) – Enabling/disabling async connect mode. #### WARNING Experience shows that this is a delicate setting. Even with a newer OpenLDAP, the TLS library version used by libldap might be unable to handle non-blocking sockets correctly. ``` -------------------------------- ### Enable ManageDsaIT Control Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Enables the ManageDsaIT control to treat LDAP referrals as simple entries. This allows for direct manipulation of referrals as if they were standard LDAP entries. ```pycon >>> client = bonsai.LDAPClient() >>> client.set_managedsait(True) >>> conn = client.connect() >>> ref = conn.search("o=admin-ref,ou=nerdherd,dc=bonsai,dc=test", 0)[0] >>> ref {'dn': , 'objectClass': ['referral', 'extensibleObject'], 'o': ['admin-ref']} >>> type(ref) ``` -------------------------------- ### List Acquired Kerberos Tickets Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Use the klist command to view the details of acquired Kerberos tickets, including the ticket cache and service principal. ```default [noirello@bonsai.test ~]$ klist Ticket cache: FILE:/tmp/krb5cc_1000 Default principal: admin@BONSAI.TEST Valid starting Expires Service principal 22/02/16 22:06:14 23/02/16 08:06:14 krbtgt/BONSAI.TEST@BONSAI.TEST renew until 23/02/16 22:06:12 ``` -------------------------------- ### Retrieve Supported SASL Mechanisms Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Check the root DSE's supportedSASLMechanisms value to see which authentication mechanisms are available on the server. ```default >>> client.get_rootDSE()['supportedSASLMechanisms'] ['GSS-SPNEGO', 'GSSAPI', 'DIGEST-MD5', 'NTLM'] ``` -------------------------------- ### Simple LDAP Search and Modify Source: https://github.com/noirello/bonsai/blob/master/README.rst Demonstrates how to perform a simple search for an LDAP entry and then modify its 'givenname' and 'sn' attributes. This snippet is suitable for synchronous operations. ```python import bonsai client = bonsai.LDAPClient("ldap://localhost") client.set_credentials("SIMPLE", user="cn=admin,dc=bonsai,dc=test", password="secret") with client.connect() as conn: res = conn.search("ou=nerdherd,dc=bonsai,dc=test", 2, "(cn=chuck)") res[0]['givenname'] = "Charles" res[0]['sn'] = "Carmichael" res[0].modify() ``` -------------------------------- ### NTLM Authentication Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Use NTLM for challenge-response authentication. This mechanism requires a username and password. ```default >>> client.set_credentials("NTLM", "user", "secret") >>> client.connect().whoami() "dn:cn=user,dc=bonsai,dc=test" ``` -------------------------------- ### Optimize Async Evaluation with I/O Events Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Improve the asynchronous evaluation method by using Curio's I/O event waiting instead of constant polling. This leverages the socket's file descriptor to wait for data availability. ```python async def _evaluate(self, msg_id: int, timeout: Optional[float] = None): while True: await curio.traps._write_wait(self.fileno()) res = self.get_result(msg_id) if res is not None: return res ``` -------------------------------- ### Parse Active Directory Security Descriptor Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Use `SecurityDescriptor.from_binary()` to parse the `ntSecurityDescriptor` attribute from an Active Directory entry. This requires an established LDAP connection and a retrieved AD entry. ```python >>> import bonsai >>> from bonsai.active_directory import SecurityDescriptor >>> client = LDAPClient("ldap://localhost") >>> client.set_credentials("SIMPLE", user="cn=chuck,ou=nerdherd,dc=bonsai,dc=test", password="secret") >>> conn = client.connect() >>> entry = conn.search("cn=chuck,ou=nerdherd,dc=bonsai,dc=test", 0, attrlist=["ntSecurityDescriptor"])[0] >>> sec_desc = SecurityDescriptor.from_binary(entry["ntSecurityDescriptor"][0]) >>> print(sec_desc.owner_sid) >>> print(sec_desc.dacl) ``` -------------------------------- ### Authenticate with GSSAPI Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Authenticate to the LDAP server using the GSSAPI mechanism after acquiring a TGT. The 'whoami' method verifies the authenticated identity. ```python import bonsai client = bonsai.LDAPClient("ldap://bonsai.test") client.set_credentials("GSSAPI") client.connect().whoami() 'dn:cn=admin,dc=bonsai,dc=test' ``` -------------------------------- ### Writing an LDAP entry to an LDIF file Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Use LDIFWriter to write an LDAPEntry object to an LDIF file. The file must be opened in write mode. ```python3 from bonsai import LDAPClient from bonsai import LDIFWriter client = LDAPClient("ldap://bonsai.test") with client.connect() as conn: res = conn.search("cn=jeff,ou=nerdherd,dc=bonsai,dc=test", 0) with open("user.ldif", "w") as data: writer = LDIFWriter(data) writer.write_entry(res[0]) ``` -------------------------------- ### Set Windows Event Loop Policy for Asyncio Source: https://github.com/noirello/bonsai/blob/master/docs/api.md On Windows, the default asyncio event loop changed in Python 3.8. Bonsai's asyncio connection requires the older SelectorEventLoop. Ensure this policy is set before using the module to avoid NotImplementedError. ```python if sys.platform == 'win32': import asyncio asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) ``` -------------------------------- ### bonsai.set_debug(debug, level=0) Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Enables or disables debug mode for the module. When enabled, it provides traceback information of C function calls. ```APIDOC ## bonsai.set_debug(debug, level=0) Set debug mode for the module. Turning it on will provide traceback information of C function calls on the standard output. If the module uses OpenLDAP, then setting the level parameter to a non-zero integer will also give additional info about the libldap function calls. * **Parameters:** * **debug** (*bool*) – Enabling/disabling debug mode. * **level** (*int*) – The debug level (for OpenLDAP only). ``` -------------------------------- ### Perform Anonymous Bind and Whoami Source: https://github.com/noirello/bonsai/blob/master/docs/tutorial.md After an anonymous bind, use the 'whoami()' operation to retrieve the identity of the authenticated user. This will typically return 'anonymous' for an anonymous bind. ```python >>> conn.whoami() 'anonymous' ``` -------------------------------- ### Enable Password Policy Control Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Enables password policy control to receive password expiration and grace login information. This changes the return value of connect methods to include control details. ```pycon >>> import bonsai >>> client = bonsai.LDAPClient() >>> client.set_credentials("SIMPLE", "cn=user,dc=bonsai,dc=test", "secret") >>> client.set_password_policy(True) >>> conn, ctrl = client.connect() >>> conn >>> ctrl {'grace': 1, 'expire': 3612, 'oid': '1.3.6.1.4.1.42.2.27.8.5.1'} ``` -------------------------------- ### Virtual List View (VLV) Control Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Utilize the Virtual List View control for precise retrieval of entries around a target entry within a large, ordered list. This control requires server-side sort and specifies counts before and after the target. The estimated list count must also be provided. ```python >>> conn.virtual_list_search("ou=nerdherd,dc=bonsai,dc=test", 2, attrlist=['cn', 'uidNumber'], sort_order=['-uidNumber'], offset=4, before_count=1, after_count=1, est_list_count=6) ([{'dn': , 'cn': ['sam'], 'uidNumber': [4]}, {'dn': , 'cn': ['skip'], 'uidNumber': [3]}, {'dn': , 'cn': ['jeff'], 'uidNumber': [2]}], {'oid': '2.16.840.1.113730.3.4.10', 'target_position': 4, 'list_count': 7}) ``` -------------------------------- ### ConnectionPool Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Manages a pool of LDAP connections, allowing for efficient reuse of connections. ```APIDOC ## ConnectionPool Manages a pool of LDAP connections, allowing for efficient reuse of connections. ### Example Usage: ```python import bonsai from bonsai.pool import ConnectionPool client = bonsai.LDAPClient() pool = ConnectionPool(client) with pool.spawn() as conn: print(conn.whoami()) ``` ``` -------------------------------- ### ThreadedConnectionPool Source: https://github.com/noirello/bonsai/blob/master/docs/api.md A connection pool specifically designed for threaded environments. ```APIDOC ## ThreadedConnectionPool A connection pool specifically designed for threaded environments. ``` -------------------------------- ### LDIFWriter Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Provides functionality to write data in LDIF format. ```APIDOC ## LDIFWriter Provides functionality to write data in LDIF format. ``` -------------------------------- ### bonsai.get_tls_impl_name() Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Retrieves the name of the underlying TLS implementation used by the LDAP library. ```APIDOC ## bonsai.get_tls_impl_name() Return the identification of the underlying TLS implementation that is used by the LDAP library: ```pycon >>> bonsai.get_tls_impl_name() "MozNSS" ``` The possible return values are: GnuTLS, OpenSSL, MozNSS and SChannel. * **Returns:** A identification of TLS implementation. * **Return type:** str ``` -------------------------------- ### Authenticate with GSSAPI and Specify Authorization ID Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Authenticate using GSSAPI and explicitly set the authorization ID. This is useful for specifying a different identity for authorization than the authenticated principal. ```python client.set_credentials("GSSAPI", authz_id="u:chuck") client.connect().whoami() 'dn:cn=chuck,ou=nerdherd,dc=bonsai,dc=test' ``` -------------------------------- ### LDAPEntry Class Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Represents an LDAP entry, which is a collection of attributes associated with a distinguished name. ```APIDOC ## LDAPEntry Class ### Description Represents an LDAP entry. ### Initialization `LDAPEntry(dn)`: Initializes an LDAPEntry with a distinguished name. ### Attributes - `connection`: The LDAPConnection object associated with the entry. - `dn`: The distinguished name of the entry. ### Methods - `clear_attribute_changes()`: Clears attribute changes to get a clean state. - `items()`: Returns a view of the entry's items (attributes and values). - `keys()`: Returns a view of the entry's keys (attribute names). - `values()`: Returns a view of the entry's values. ### Usage ```python from bonsai import LDAPEntry anna = LDAPEntry('cn=anna,ou=nerdherd,dc=bonsai,dc=test') print(anna.dn) print(str(anna.dn)) ``` ``` -------------------------------- ### Modify an Existing LDAP Entry Source: https://github.com/noirello/bonsai/blob/master/docs/tutorial.md Shows how to modify an existing `LDAPEntry` by adding new attributes, appending values to existing attributes, or deleting attributes. Modifications are saved to the server by calling the `LDAPEntry.modify()` method. ```pycon >>> anna['givenName'] = "Anna" # Set new givenName attribute. >>> anna['cn'].append('wu') # Add new common name attribute without remove the already set ones. >>> del anna['mail'] # Remove all values of the mail attribute. >>> anna.modify() True ``` -------------------------------- ### DIGEST-MD5 Authentication with Authorization ID Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Use DIGEST-MD5 for challenge-response authentication, optionally with an authorization ID to perform operations under a different identity. ```pycon >>> client.set_credentials("DIGEST-MD5", "user", "secret", authz_id="u:root") >>> client.connect().whoami() "dn:cn=admin,dc=bonsai,dc=test" ``` -------------------------------- ### Writing LDAP entry changes to an LDIF file Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Use LDIFWriter to serialize and write changes made to an LDAPEntry object to an LDIF file. The file must be opened in write mode. ```python3 from bonsai import LDAPClient from bonsai import LDIFWriter client = LDAPClient("ldap://bonsai.test") with client.connect() as conn: res = conn.search("cn=jeff,ou=nerdherd,dc=bonsai,dc=test", 0) # Make some changes on the entry. res[0]["mail"].append("jeff_secondary@mail.test") res[0]["homeDirectory"] = "/opt/jeff" with open("changes.ldif", "w") as data: writer = LDIFWriter(data) writer.write_changes(res[0]) ``` -------------------------------- ### Reading an LDIF file Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Use LDIFReader to iterate over entries in an LDIF file. Ensure the file is opened in read mode. ```python3 from bonsai import LDIFReader with open("users.ldif", "r") as data: reader = LDIFReader(data) for ent in reader: print(ent) ``` -------------------------------- ### Parse UserAccountControl Attribute Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Instantiate `UserAccountControl` with the raw attribute value to easily access user account properties. This provides a dictionary of boolean flags representing the account's status. ```python >>> from bonsai.active_directory import UserAccountControl >>> uac = UserAccountControl(entry['userAccountControl'][0]) >>> uac >>> uac.properties {'script': False, 'accountdisable': False, 'homedir_required': False, 'lockout': False, 'passwd_notreqd': False, 'passwd_cant_change': False, 'encrypted_text_pwd_allowed': False, 'temp_duplicate_account': False, 'normal_account': True, 'interdomain_trust_account': False, 'workstation_trust_account': False, 'server_trust_account': False, 'dont_expire_password': True, 'mns_logon_account': False, 'smartcard_required': False, 'trusted_for_delegation': False, 'not_delegated': False, 'use_des_key_only': False, 'dont_req_preauth': False, 'password_expired': False, 'trusted_to_auth_for_delegation': False, 'partial_secrets_account': False} ``` -------------------------------- ### LDAPConnection.virtual_list_search Source: https://github.com/noirello/bonsai/blob/master/docs/api.md Performs a search using virtual list view control. To perform the search the server side sort control has to be set with sort_order. The result set will be shifted to the offset or attrvalue and contains the specific number of entries after and before, set with after_count and before_count. ```APIDOC ## LDAPConnection.virtual_list_search ### Description Perform a search using virtual list view control. To perform the search the server side sort control has to be set with sort_order. The result set will be shifted to the offset or attrvalue and contains the specific number of entries after and before, set with after_count and before_count. The est_list_count is an estimation of the entire searched list that helps to the server to position the target entry. The result of the operation is a tuple of a list and a dictionary. The dictionary contains the VLV server response: the target position and the real list size. Thi list contains the searched entries. For further details using these controls please see [LDAP controls](advanced.md#ldap-controls). ### Method Not specified (likely a Python method call) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters: * **base** (*str*) – the base DN of the search. * **scope** (*int*) – the scope of the search. An `LDAPSearchScope` also can be used as value. * **filter_exp** (*str*) – string to filter the search in LDAP search filter syntax. * **attrlist** (*list*) – list of attribute’s names to receive only those attributes from the directory server. * **timeout** (*float*) – time limit in seconds for the search. * **sizelimit** (*int*) – the number of entries to limit the search. * **attrsonly** (*bool*) – if it’s set True, search result will contain only the name of the attributes without their values. * **sort_order** (*list*) – list of attribute’s names to use for server-side ordering, start name with ‘-’ for descending order. * **offset** (*int*) – an offset of the search result to select a target entry for virtual list view (VLV). * **before_count** (*int*) – the number of entries before the target entry for VLV. * **after_count** (*int*) – the number of entries after the target entry for VLV. * **est_list_count** (*int*) – the estimated content count of the entire list for VLV. * **attrvalue** – an attribute value (of the attribute that is used for sorting) for identifying the target entry for VLV. ### Returns: the search result. ### Return type: (list, dict) ``` -------------------------------- ### Server Side Sort Control Source: https://github.com/noirello/bonsai/blob/master/docs/advanced.md Use the server side sort control to order search results by specified attributes. Attributes prefixed with '-' denote descending order. Ensure the server supports this control. ```python >>> conn = client.connect() >>> conn.search("ou=nerdherd,dc=bonsai,dc=test", 2, sort_order=["-cn", "gn"]) ```