Skip to content

Add ldap.filter.is_filter() which checks filter syntax #272

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
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

spaceone
Copy link
Contributor

Add a utility functions ldap.filter.is_filter which can be used to check if the ldap filter has valid syntax.
This uses an unbound ldap connection, so the values are ensured to be correct via the C library.
I added test cases for the function.

@spaceone spaceone force-pushed the add-is-filter branch 2 times, most recently from 3f0a7e6 to 084b622 Compare March 30, 2019 08:59
@codecov
Copy link

codecov bot commented Mar 30, 2019

Codecov Report

Merging #272 (084b622) into master (39ea8e5) will increase coverage by 0.41%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master     #272      +/-   ##
==========================================
+ Coverage   71.30%   71.72%   +0.41%     
==========================================
  Files          50       49       -1     
  Lines        4796     5033     +237     
  Branches      802      894      +92     
==========================================
+ Hits         3420     3610     +190     
- Misses       1045     1067      +22     
- Partials      331      356      +25     
Impacted Files Coverage Δ
Lib/ldap/dn.py 98.07% <ø> (ø)
Lib/ldap/filter.py 76.74% <100.00%> (+11.11%) ⬆️
Lib/ldap/cidict.py 69.23% <0.00%> (-27.27%) ⬇️
Lib/ldap/extop/__init__.py 55.55% <0.00%> (-12.87%) ⬇️
Modules/constants.c 45.83% <0.00%> (-7.68%) ⬇️
Lib/ldap/ldapobject.py 76.38% <0.00%> (-3.85%) ⬇️
Lib/ldap/compat.py 66.03% <0.00%> (-1.24%) ⬇️
Modules/constants_generated.h 97.92% <0.00%> (-0.67%) ⬇️
Modules/LDAPObject.c 67.43% <0.00%> (-0.31%) ⬇️
Lib/ldap/schema/tokenizer.py 97.87% <0.00%> (-0.05%) ⬇️
... and 10 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2647f59...42b87b0. Read the comment docs.

@spaceone
Copy link
Contributor Author

The tests fail only due to an upstream error in spinx when building the documentation:

Traceback (most recent call last):
  File "/home/travis/build/python-ldap/python-ldap/.tox/doc/lib/python3.6/site-packages/sphinx/cmd/build.py", line 283, in build_main
    args.tags, args.verbosity, args.jobs, args.keep_going)
  File "/home/travis/build/python-ldap/python-ldap/.tox/doc/lib/python3.6/site-packages/sphinx/application.py", line 228, in __init__
    self.setup_extension(extension)
  File "/home/travis/build/python-ldap/python-ldap/.tox/doc/lib/python3.6/site-packages/sphinx/application.py", line 380, in setup_extension
    self.registry.load_extension(self, extname)
  File "/home/travis/build/python-ldap/python-ldap/.tox/doc/lib/python3.6/site-packages/sphinx/registry.py", line 484, in load_extension
    metadata = mod.setup(app)
  File "/home/travis/build/python-ldap/python-ldap/.tox/doc/lib/python3.6/site-packages/sphinxcontrib/spelling/__init__.py", line 11, in setup
    app.info('Initializing Spelling Checker')
AttributeError: 'Sphinx' object has no attribute 'info'

Copy link
Member

@tiran tiran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm -1 on this approach.

It seems rather wasteful and slow to create a LDAP connection object to verify that a string is a valid filter. The call will also connect to the default ldap server from /etc/openldap/ldap.conf on success. This will spam the error log of the default LDAP server.

ldap_pvt_put_filter might work, but the function is not exposed in ldap.h.

@spaceone
Copy link
Contributor Author

@tiran
On my notebook, no LDAP connection is done with this and not required. It works even without any network connection. And it is very fast.

Would it create a ldap connection if I had a /etc/openldap/ldap.conf? Then I will see if I can find a way to prevent this.

@encukou
Copy link
Member

encukou commented Apr 1, 2019

If there is no connection involved, why catch SERVER_DOWN?
This logic looks fragile: if the server is down, all filters are considered valid.

@spaceone
Copy link
Contributor Author

spaceone commented Apr 2, 2019

If the ldap object is uninitialized and unconnected it raises ldap.SERVER_DOWN without any connection attempt.
Using strace I cannot see any connection attempt:
strace python -c 'import ldap; lo = ldap.initialize(""); lo.search_ext_s("", ldap.SCOPE_BASE, "foo=bar")' 2>&1 | less

What I can see is that it reads my "/etc/ldap/ldap.conf" and tries to read ~/.ldaprc.
My ldap.conf contains URI and BASE. Do I need HOST so that it would actually try to connect even if I pass "" as URI?

@encukou
Copy link
Member

encukou commented May 24, 2019

The main problem is that, however you connect, on SERVER_DOWN, all filters would be considered valid – the function would return a wrong result with no indication of failure.

encukou pushed a commit to encukou/python-ldap that referenced this pull request Sep 20, 2019
@encukou
Copy link
Member

encukou commented Sep 20, 2019

I don't want to merge code that can silently hide a failure, so I'll reject this approach. It might be suitable for a library that builds on top of python-ldap, rather than for python-ldap itself.
Please re-open if you want to discuss further.

@encukou encukou closed this Sep 20, 2019
@spaceone
Copy link
Contributor Author

There is no REOPEN button.
As far as I inspected this, your assumptions that there will be a ldap connection established, are not correct. Can you proove me wrong, please, and explain what I need to do so that this behavior would occurr?

@quanah
Copy link
Contributor

quanah commented Sep 20, 2019

If you set LDAPNOINIT the client libraries will bypass reading ldap.conf/.ldaprc. This is what the OpenLDAP test suite does to ensure that local client configurations don't interfere.

@tiran
Copy link
Member

tiran commented Sep 20, 2019

In my opinion the patch is not a clean solution but a hack. I don't like to add such hacks to python-ldap. You can easily ship the short function in your application.

I wouldn't mind to add this feature iff OpenLDAP adds a public API to perform the check.

@encukou encukou reopened this Sep 20, 2019
@encukou
Copy link
Member

encukou commented Sep 20, 2019

There is no REOPEN button.

Apologies, I had wrong assumptions of the GitHub UI.


My main objection is that is_filter returns different results based on whether the server is reachable or not.
If that is not the case, why does the code handle ldap.SERVER_DOWN?

@spaceone
Copy link
Contributor Author

spaceone commented Sep 20, 2019

I wouldn't mind to add this feature iff OpenLDAP adds a public API to perform the check.

Yes, that would be the best. You already mentioned the not exposed function ldap_pvt_put_filter.

My main objection is that is_filter returns different results based on whether the server is reachable or not.
If that is not the case, why does the code handle ldap.SERVER_DOWN?

The ldap connection there is not connected and will never be. At least I could not get any connection attempt with that code. ldap.SERVER_DOWN therefore does only indicate that the filter is valid, no matter if there is a server which can be connected to or not. The filter validity is checked before any connection attempts, it there are any at all.

If that code really does a connection, how would I set this up to reproduce this? We are using the above code since years and it works. We have a /etc/openldap/ldap.conf and a /etc/ldap/ldap.conf with valid entries. I don't see where it's evaluated. and we did not set LDAPNOINIT.

@spaceone
Copy link
Contributor Author

I took the name is_filter() for consistency, because there is also a is_dn() already.

encukou added a commit that referenced this pull request Jan 29, 2020
#303
Suggested in #272

Co-authored-by: Florian Best <spaceone@users.noreply.github.com>
@encukou
Copy link
Member

encukou commented Jan 29, 2020

I see. I misunderstood the code.

Still, this does rely on undocumented behavior, so @tiran's comment holds:

In my opinion the patch is not a clean solution but a hack. I don't like to add such hacks to python-ldap. You can easily ship the short function in your application.

I wouldn't mind to add this feature iff OpenLDAP adds a public API to perform the check.

IOW, if this is the easiest way to check filter validity using OpenLDAP, it's an issue in OpenLDAP. python-ldap is the wrong place for a workaround.

@tiran
Copy link
Member

tiran commented May 27, 2020

Did you raise the issue with OpenLDAP upstream to request a filter validation function? I'm hesitant to use an internal, private function ldap_pvt_put_filter to validate filters.

@spaceone
Copy link
Contributor Author

Sorry, I did not see your latest answer. I created a feature request at openldap: https://bugs.openldap.org/show_bug.cgi?id=9393
Let's see what they are saying.

I also updated the pull request, made it more clear that ldap.SERVER_DOWN is expected to be raised and raise a RuntimeError (which can't happen) in all other cases. Maybe you are fine with this implementation and could provide it as fallback until there is something available in Open-LDAP? The function has detailed tests.

@spaceone
Copy link
Contributor Author

spaceone commented Oct 1, 2021

They aren't replying at all.

@quanah
Copy link
Contributor

quanah commented Oct 14, 2021

They aren't replying at all.

What you filed is a feature request. The earliest it would be done is OpenLDAP 2.7.

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

Successfully merging this pull request may close these issues.

4 participants