Skip to content

Commit d9459db

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 c86ff40 commit d9459db

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
@@ -138,15 +138,33 @@ is what most shell username validators do.
138138

139139
#### `LDAPAuthenticator.use_ssl`
140140

141-
Boolean to specify whether to use SSL encryption when contacting
142-
the LDAP server. If it is left to `False` (the default)
143-
`LDAPAuthenticator` will try to upgrade connection with StartTLS.
144-
Set this to be `True` to start SSL connection.
141+
`use_ssl` is deprecated since 2.0. `use_ssl=True` translates to configuring
142+
`tls_strategy="on_connect"`, but `use_ssl=False` (previous default) doesn't
143+
translate to anything.
144+
145+
#### `LDAPAuthenticator.tls_strategy`
146+
147+
When LDAPAuthenticator connects to the LDAP server, it can establish a
148+
SSL/TLS connection directly, or do it before binding, which is LDAP
149+
terminology for authenticating and sending sensitive credentials.
150+
151+
The protocol LDAPv3 deprecated establishing a SSL/TLS connection
152+
directly (`tls_strategy="on_connect"`) in favor of upgrading the
153+
connection to SSL/TLS before binding (`tls_strategy="before_bind"`).
154+
155+
Supported `tls_strategy` values are: - "before_bind" (default) -
156+
"on_connect" (deprecated in LDAPv3, associated with use of port 636) -
157+
"insecure"
158+
159+
When configuring `tls_strategy="on_connect"`, the default value of
160+
`server_port` becomes 636.
145161

146162
#### `LDAPAuthenticator.server_port`
147163

148-
Port to use to contact the LDAP server. Defaults to 389 if no SSL
149-
is being used, and 636 is SSL is being used.
164+
Port on which to contact the LDAP server.
165+
166+
Defaults to `636` if `tls_strategy="on_connect"` is set, `389`
167+
otherwise.
150168

151169
#### `LDAPAuthenticator.user_search_base`
152170

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

@@ -297,14 +347,26 @@ def resolve_username(self, username_supplied_by_user):
297347
return (user_dn, response[0]["dn"])
298348

299349
def get_connection(self, userdn, password):
350+
if self.tls_strategy == TlsStrategy.on_connect:
351+
use_ssl = True
352+
auto_bind = ldap3.AUTO_BIND_NO_TLS
353+
elif self.tls_strategy == TlsStrategy.before_bind:
354+
use_ssl = False
355+
auto_bind = ldap3.AUTO_BIND_TLS_BEFORE_BIND
356+
else: # TlsStrategy.insecure
357+
use_ssl = False
358+
auto_bind = ldap3.AUTO_BIND_NO_TLS
359+
300360
server = ldap3.Server(
301-
self.server_address, port=self.server_port, use_ssl=self.use_ssl
302-
)
303-
auto_bind = (
304-
ldap3.AUTO_BIND_NO_TLS if self.use_ssl else ldap3.AUTO_BIND_TLS_BEFORE_BIND
361+
self.server_address,
362+
port=self.server_port,
363+
use_ssl=use_ssl,
305364
)
306365
conn = ldap3.Connection(
307-
server, user=userdn, password=password, auto_bind=auto_bind
366+
server,
367+
user=userdn,
368+
password=password,
369+
auto_bind=auto_bind,
308370
)
309371
return conn
310372

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)