[WIP] add URL support to constructor, some smarter TLS defaults
fixes #246 (hi @danleyden), relevant RFCs are 2255 (obsolete) and 4516. This is in-progress because I haven't updated the class docs or tests yet. Before I do, I wanted to open the PR and make sure I'm generally on the right track. Example:
Net::LDAP.new(url: 'ldaps://ldap01.example.com:9636/dc=example,dc=com',
auth: {method: :simple, username: <bind_dn>, password: <bind_pw>})
#
# is the same as
#
Net::LDAP.new(host: 'ldap01.example.com',
port: 9636,
base: 'dc=example,dc=com',
encryption: {method: :simple_tls,
tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS },
auth: {method: :simple, username: <bind_dn>, password: <bind_pw>})
but wait, there's more!
Net::LDAP.new(url: 'ldap://ldap01.example.com',
encryption: true)
#
# is the same as
#
Net::LDAP.new(host: 'ldap01.example.com',
encryption: {method: :start_tls,
tls_options: :default })
#
# or if you're not into the whole brevity thing
#
Net::LDAP.new(host: 'ldap01.example.com',
encryption: {method: :start_tls
tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS })
TLS changes
- If you set
encryption: true(previously a noop), the internal@encryptionhash gets populated with the OpenSSL defaults. Those defaults setVERIFY_PEER(cert validation!) and use the default OS-provided CA certificate store. Yay easy cert validation! - To be explicit: the new
encryption: trueargument does NOT require the use ofurl. If you don't set anurl,method: :start_tlsvs:simple_tlsis set by looking at the port. 636 is:simple_tls, everything else is:start_tls. -
tls_options: :defaultis a shortcut fortls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS. That string is too long.
More rambling
Aside from search base DN, the LDAP URL RFCs also allow you to specify a constrained list of attributes to return on search results, a scope (sub/one/all), and a search filter. A default scope for searches seems like a reasonable thing for Net::LDAP instances to track, similar to base DN, but it doesn't currently so I didn't add it. I could see an argument for writing a Net::LDAP#search_by_url, or extending the existing Net::LDAP#search, but I think this gem has higher priorities.
Credentials: The RFCs don't say anything about accepting username/pw befor the hostname (e.g. http://user:[email protected]/), and it's in formal ABNF and everything, so I've avoided handling those. I don't particularly want to encourage people to put passwords in URLs.
You'll note from the rubocop config change that rubocop started complaining about Net::LDAP's length. I think we can shrink that considerably by moving a ton of constants into their own class/module. That's getting to be a more major change, though, so next time.
/cc @jch
General note: My intention here is for this to be a noop for existing code. Specifically, this PR should NOT change the cert validation behavior introduced in #279. If you don't specify an URL and don't set encryption: true, no cert validation by default. If you use an ldaps:// URL, or encryption: true, or tls_options: :default, you get cert validation.
@tmaher not looked at the code, but the examples look to be exactly what I was meaning.
One further improvement that would add to my happiness would be supporting multiple urls. Currently, I believe that net-ldap supports providing multiple ldap servers through providing hosts rather than host.
Would it be possible to support multiple URLs? I suspect that there would likely need to be some error checking to detect if more than the host/port/protocol combination were different and raise appropriate errors.
Of course, not having double checked the code, that might already be there.
Thanks - this will help us get rid of a load of nasty code in our app
If you set encryption: true (previously a noop), the internal @encryption hash gets populated with the OpenSSL defaults. Those defaults set VERIFY_PEER (cert validation!) and use the default OS-provided CA certificate store. Yay easy cert validation!
Maybe I missed this in #279, but was there a breaking change in the encryption: param behavior in either this or the other PR? Something we should note in the changelog before we release.
A default scope for searches seems like a reasonable thing for Net::LDAP instances to track, similar to base DN, but it doesn't currently so I didn't add it
:+1: Sounds reasonable
I could see an argument for writing a Net::LDAP#search_by_url, or extending the existing Net::LDAP#search, but I think this gem has higher priorities
Agreed. Even though the spec allows it, I'd rather not introduce a new public interface for search.
The RFCs don't say anything about accepting username/pw befor the hostname (e.g. http://user:[email protected]/), and it's in formal ABNF and everything, so I've avoided handling those. I don't particularly want to encourage people to put passwords in URLs.
:+1: I can imagine some complaints about this, perhaps it'd be worth documenting
You'll note from the rubocop config change that rubocop started complaining about Net::LDAP's length. I think we can shrink that considerably by moving a ton of constants into their own class/module. That's getting to be a more major change, though, so next time.
Thanks for being mindful of keeping pull requests focused. It makes them easier to review
Would it be possible to support multiple URLs? I suspect that there would likely need to be some error checking to detect if more than the host/port/protocol combination were different and raise appropriate errors.
I would prefer this to go into a separate PR if you do decide to implement it.
@tmaher could you add some tests for this?