8000 Server object seems to be not thread-safe · Issue #1174 · cannatag/ldap3 · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Server object seems to be not thread-safe #1174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jelinaunger opened this issue Mar 5, 2025 · 0 comments
Open

Server object seems to be not thread-safe #1174

jelinaunger opened this issue Mar 5, 2025 · 0 comments

Comments

@jelinaunger
Copy link

I have multiple threads accessing the same server object. Each thread creates a new connection to the same server. In the search operation, the schema from the server is accessed by the connection.

request = search_operation(search_base,
search_filter,
search_scope,
dereference_aliases,
attributes,
size_limit,
time_limit,
types_only,
self.auto_escape if auto_escape is None else auto_escape,
self.auto_encode,
self.server.schema if self.server else None,
validator=self.server.custom_validator,
check_names=self.check_names)

In the bind operation of a new connection, the server info is refreshed. The schema is temporarily set to None before the new schema is set.

self._schema_info = None

For the write operation, a lock is used. However, the read operation is not protected. If another thread now reads the schema it is None and the attributes from the search operation can not be decoded properly. The attributes dictionary is equal to the raw_attributes dictionary. For example, uuids are returned in bytes instead of a string.

Here is an example to reproduce the behavior:

server = ldap3.Server("ldaps://test.ldap", connect_timeout=5, get_info=ldap3.SCHEMA)
conn_opts = {'auto_bind': None,
             'client_strategy': ldap3.SAFE_SYNC,
             'authentication': ldap3.SIMPLE,
             'user': '****',
             'password': '****'}

def search():
    connection = ldap3.Connection(server, **conn_opts)
    connection.bind()

    _, _, response, _ = connection.search(search_base="dc=test,dc=ldap",
                                          search_scope=ldap3.SUBTREE,
                                          search_filter="(&(sAMAccountName=*)(objectClass=person)(sAMAccountName=foo))",
                                          attributes=["objectGUID"])
    result = response[0].get('attributes')
    if isinstance(result["objectGUID"], list) and isinstance(result["objectGUID"][0], bytes):
        print(f"UUID is a byte array!")

    connection.unbind()

thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=8)
for i in range(50):
    thread_pool.submit(search)
thread_pool.shutdown(wait=True)

I tried this with different sync strategies for the connection and it occurred for all of them, even for the thread-safe strategies e.g. SAFE_SYNC. It seems that these strategies are only intended to protect the connection object if it is shared over multiple threads. Is that correct?

A simple solution would be to use the lock also in the read operation of the schema:

@property
    def schema(self):
        with self.dit_lock:
            return self._schema_info

This solved my issue, however I did not test it for other configs.

Am I missing something or is it not intended to share the server object over multiple threads?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant
0