Skip to content

Commit 36ea8ef

Browse files
committed
Add tls_strategy and deprecate use_ssl
With `tls_strategy` we have three choices on how to secure our connection to the LDAP server. - "on_connect", it is "use_ssl=True" implied - "before_bind", it is what "use_ssl=False" implied - "insecure", this wasn't an option before, but has been requested by users.
1 parent 686983c commit 36ea8ef

File tree

3 files changed

+117
-18
lines changed

3 files changed

+117
-18
lines changed

README.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,33 @@ is what most shell username validators do.
167167

168168
#### `LDAPAuthenticator.use_ssl`
169169

170-
Boolean to specify whether to use SSL encryption when contacting
171-
the LDAP server. If it is left to `False` (the default)
172-
`LDAPAuthenticator` will try to upgrade connection with StartTLS.
173-
Set this to be `True` to start SSL connection.
170+
`use_ssl` is deprecated since 2.0. `use_ssl=True` translates to configuring
171+
`tls_strategy="on_connect"`, but `use_ssl=False` (previous default) doesn't
172+
translate to anything.
173+
174+
#### `LDAPAuthenticator.tls_strategy`
175+
176+
When LDAPAuthenticator connects to the LDAP server, it can establish a
177+
SSL/TLS connection directly, or do it before binding, which is LDAP
178+
terminology for authenticating and sending sensitive credentials.
179+
180+
The protocol LDAPv3 deprecated establishing a SSL/TLS connection
181+
directly (`tls_strategy="on_connect"`) in favor of upgrading the
182+
connection to SSL/TLS before binding (`tls_strategy="before_bind"`).
183+
184+
Supported `tls_strategy` values are: - "before_bind" (default) -
185+
"on_connect" (deprecated in LDAPv3, associated with use of port 636) -
186+
"insecure"
187+
188+
When configuring `tls_strategy="on_connect"`, the default value of
189+
`server_port` becomes 636.
174190

175191
#### `LDAPAuthenticator.server_port`
176192

177-
Port to use to contact the LDAP server. Defaults to 389 if no SSL
178-
is being used, and 636 is SSL is being used.
193+
Port on which to contact the LDAP server.
194+
195+
Defaults to `636` if `tls_strategy="on_connect"` is set, `389`
196+
otherwise.
179197

180198
#### `LDAPAuthenticator.user_search_base`
181199

ldapauthenticator/ldapauthenticator.py

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
1+
import enum
12
import re
23

34
import ldap3
45
from jupyterhub.auth import Authenticator
56
from ldap3.utils.conv import escape_filter_chars
6-
from traitlets import Bool, Int, List, Unicode, Union, validate
7+
from traitlets import Bool, Int, List, Unicode, Union, UseEnum, observe, validate
8+
9+
10+
class TlsStrategy(enum.Enum):
11+
"""
12+
Represents a SSL/TLS strategy for LDAPAuthenticator to use when interacting
13+
with the LDAP server.
14+
"""
15+
16+
before_bind = 1
17+
on_connect = 2
18+
insecure = 3
719

820

921
class LDAPAuthenticator(Authenticator):
@@ -20,23 +32,61 @@ class LDAPAuthenticator(Authenticator):
2032
help="""
2133
Port on which to contact the LDAP server.
2234
23-
Defaults to `636` if `use_ssl` is set, `389` otherwise.
35+
Defaults to `636` if `tls_strategy="on_connect"` is set, `389`
36+
otherwise.
2437
""",
2538
)
2639

2740
def _server_port_default(self):
28-
if self.use_ssl:
41+
if self.tls_strategy == TlsStrategy.on_connect:
2942
return 636 # default SSL port for LDAP
3043
else:
3144
return 389 # default plaintext port for LDAP
3245

3346
use_ssl = Bool(
34-
False,
47+
None,
48+
allow_none=True,
49+
config=True,
50+
help="""
51+
`use_ssl` is deprecated since 2.0. `use_ssl=True` translates to configuring
52+
`tls_strategy="on_connect"`, but `use_ssl=False` (previous default) doesn't
53+
translate to anything.
54+
""",
55+
)
56+
57+
@observe("use_ssl")
58+
def _observe_use_ssl(self, change):
59+
if change.new:
60+
self.tls_strategy = TlsStrategy.on_connect
61+
self.log.warning(
62+
"LDAPAuthenticator.use_ssl is deprecated in 2.0 in favor of LDAPAuthenticator.tls_strategy, "
63+
'instead of configuring use_ssl=True, configure use tls_strategy="on_connect" from now on.'
64+
)
65+
else:
66+
self.log.warning(
67+
"LDAPAuthenticator.use_ssl is deprecated in 2.0 in favor of LDAPAuthenticator.tls_strategy, "
68+
"you can stop configuring use_ssl=False from now on as doing so has no effect."
69+
)
70+
71+
tls_strategy = UseEnum(
72+
TlsStrategy,
73+
default_value=TlsStrategy.before_bind,
3574
config=True,
3675
help="""
37-
Use SSL to communicate with the LDAP server.
76+
When LDAPAuthenticator connects to the LDAP server, it can establish a
77+
SSL/TLS connection directly, or do it before binding, which is LDAP
78+
terminology for authenticating and sending sensitive credentials.
79+
80+
The protocol LDAPv3 deprecated establishing a SSL/TLS connection
81+
directly (`tls_strategy="on_connect"`) in favor of upgrading the
82+
connection to SSL/TLS before binding (`tls_strategy="before_bind"`).
83+
84+
Supported `tls_strategy` values are: - "before_bind" (default) -
85+
"on_connect" (deprecated in LDAPv3, associated with use of port 636) -
86+
"insecure"
3887
39-
Deprecated in version 3 of LDAP. Your LDAP server must be configured to support this, however.
88+
When configuring `tls_strategy="on_connect"`, the default value of
89+
`server_port` becomes 636.
4090
""",
4191
)
4292

@@ -314,14 +364,26 @@ def resolve_username(self, username_supplied_by_user):
314364
return (user_dn, response[0]["dn"])
315365

316366
def get_connection(self, userdn, password):
367+
if self.tls_strategy == TlsStrategy.on_connect:
368+
use_ssl = True
369+
auto_bind = ldap3.AUTO_BIND_NO_TLS
370+
elif self.tls_strategy == TlsStrategy.before_bind:
371+
use_ssl = False
372+
auto_bind = ldap3.AUTO_BIND_TLS_BEFORE_BIND
373+
else: # TlsStrategy.insecure
374+
use_ssl = False
375+
auto_bind = ldap3.AUTO_BIND_NO_TLS
376+
317377
server = ldap3.Server(
318-
self.server_address, port=self.server_port, use_ssl=self.use_ssl
319-
)
320-
auto_bind = (
321-
ldap3.AUTO_BIND_NO_TLS if self.use_ssl else ldap3.AUTO_BIND_TLS_BEFORE_BIND
378+
self.server_address,
379+
port=self.server_port,
380+
use_ssl=use_ssl,
322381
)
323382
conn = ldap3.Connection(
324-
server, user=userdn, password=password, auto_bind=auto_bind
383+
server,
384+
user=userdn,
385+
password=password,
386+
auto_bind=auto_bind,
325387
)
326388
return conn
327389

ldapauthenticator/tests/test_ldapauthenticator.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,26 @@ async def test_ldap_auth_blank_template(authenticator):
5858

5959
async def test_ldap_auth_ssl(authenticator):
6060
authenticator.use_ssl = True
61-
authenticator.server_port = 636
61+
62+
# proper username and password in allowed group
63+
authorized = await authenticator.get_authenticated_user(
64+
None, {"username": "fry", "password": "fry"}
65+
)
66+
assert authorized["name"] == "fry"
67+
68+
69+
async def test_ldap_auth_tls_strategy_on_connect(authenticator):
70+
authenticator.tls_strategy = "on_connect"
71+
72+
# proper username and password in allowed group
73+
authorized = await authenticator.get_authenticated_user(
74+
None, {"username": "fry", "password": "fry"}
75+
)
76+
assert authorized["name"] == "fry"
77+
78+
79+
async def test_ldap_auth_tls_strategy_insecure(authenticator):
80+
authenticator.tls_strategy = "insecure"
6281

6382
# proper username and password in allowed group
6483
authorized = await authenticator.get_authenticated_user(

0 commit comments

Comments
 (0)