nickw444 / flask-ldap3-login

LDAP3 Logins for Flask/Flask-Login

Home Page:http://flask-ldap3-login.readthedocs.org/en/latest/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

get_object() throws a KeyError exception when user not found

Yakuza-UA opened this issue · comments

This issue only happens whenever I want to user get_user_info_for_username() and search applies to Base DN only (i.e. User DN is None). The way our AD is structured is there are following elements at its root

  • DC=domain,DC=com
    all user and group OUs/CNs are here
    • OU=Global Users
    • OU=Privileged Users
    • etc
  • CN=Configuration,DC=domain,DC=com
  • CN=Schema,CN=Configuration,DC=domain,DC=com
  • DC=ForestDnsZones,DC=domain,DC=com
  • DC=DomainDnsZones,DC=domain,DC=com

Unfortunately, if I use get_user_info_for_username(username) it throws the following error:

  File "./app/forms.py", line 159, in __call__
    portalUser = ldap3.get_user_info_for_username(field.data)
  File "/home/admin/venv/byod-management/lib/python3.7/site-packages/flask_ldap3_login/__init__.py", line 639, in get_user_info_for_username
    _connection=_connection,
  File "/home/admin/venv/byod-management/lib/python3.7/site-packages/flask_ldap3_login/__init__.py", line 702, in get_object
    data = connection.response[0]['attributes']
KeyError: 'attributes'

I had to modify source code of get_object() as per below to get some debug data:

        log.error(dn)
        log.error(filter)
        log.error(attributes)

        data = None
        if len(connection.response) > 0:
            log.error(connection.response)
            data = connection.response[0]['attributes']

It returned the following

ERROR:flask_ldap3_login:DC=domain,DC=com
ERROR:flask_ldap3_login:(&(sAMAccountName=asdasd)(objectclass=person))
ERROR:flask_ldap3_login:*
ERROR:flask_ldap3_login:[{'uri': ['ldap://DomainDnsZones.domain.com/DC=DomainDnsZones,DC=domain,DC=com'], 'type': 'searchResRef'}, {'uri': ['ldap://ForestDnsZones.domain.com/DC=ForestDnsZones,DC=domain,DC=com'], 'type': 'searchResRef'}, {'uri': ['ldap://domain.com/CN=Configuration,DC=domain,DC=com'], 'type': 'searchResRef'}]

So, even though user does not exist under DC=domain,DC=com, LDAP plugin searches through parallel structures and returns a list as seen above. Because get_object() only checks for len(connection.response) > 0, this results into condition as if user was found. Last line in the code then raises KeyError exception because first item (in fact all those in the response) has no 'attributes' key in its list.

Would you be so kind to advise if I have to apply an alternative approach? At the moment I ended up capturing this exception in my code, but it doesn't seem CLEAN. This exception is due to Flask-LDAP3-Login not processing the data properly, imho.

I had to go through LDAP3 documentation thoroughly to understand what's going on. So... the biggest limitation of this module is that it's built around RAW response object, rather than ENTRIES. For example, here's a slightly modified code that does not suffer from the same problem

        data = None
        if len(connection.entries) > 0:
            data = connection.entries[0].entry_attributes_to_dict

Even better if it was just returning an entry object as it's so much more powerful. Few examples:

entry.entry_dn
entry.entry_attributes_as_dict
entry.entry_to_json()

Anyway, by playing with LDAP3 I figured out that this plugin is TOO heavy for my needs. Therefore I will move away to use pure LDAP3 instead. Leaving this issue open for future potential improvements. Consider using connection.entries in future code releases, even if you're going to preserve public interface, at least use this internally 😄

I think this is most likely a real bug; I've added it to the 1.0 milestone.