Skip to content

Commit

Permalink
Fix duplicated bind operation, only one is needed
Browse files Browse the repository at this point in the history
  • Loading branch information
consideRatio committed Sep 17, 2024
1 parent 9bf1cc8 commit a57aaf8
Showing 1 changed file with 38 additions and 28 deletions.
66 changes: 38 additions & 28 deletions ldapauthenticator/ldapauthenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import ldap3
from jupyterhub.auth import Authenticator
from ldap3.core.exceptions import LDAPBindError
from ldap3.utils.conv import escape_filter_chars
from ldap3.utils.dn import escape_rdn
from traitlets import Bool, Int, List, Unicode, Union, UseEnum, observe, validate
Expand Down Expand Up @@ -335,6 +336,8 @@ def resolve_username(self, username_supplied_by_user):
userdn=self.lookup_dn_search_user,
password=self.lookup_dn_search_password,
)
if not conn:
return (None, None)

search_filter = self.lookup_dn_search_filter.format(
login_attr=self.user_attribute,
Expand Down Expand Up @@ -379,9 +382,14 @@ def resolve_username(self, username_supplied_by_user):

def get_connection(self, userdn, password):
"""
Returns a ldap3 Connection object automatically bound to the user.
Returns either a ldap3 Connection object automatically bound to the
user, or None if the bind operation failed for some reason.
Raises errors on connectivity or TLS issues.
ldap3 Connection ref: https://ldap3.readthedocs.io/en/latest/connection.html
ldap3 Connection ref:
- docs: https://ldap3.readthedocs.io/en/latest/connection.html
- code: https://github.com/cannatag/ldap3/blob/dev/ldap3/core/connection.py
"""
if self.tls_strategy == TlsStrategy.on_connect:
use_ssl = True
Expand All @@ -398,13 +406,26 @@ def get_connection(self, userdn, password):
port=self.server_port,
use_ssl=use_ssl,
)
conn = ldap3.Connection(
server,
user=userdn,
password=password,
auto_bind=auto_bind,
)
return conn
try:
self.log.debug(f"Attempting to bind {userdn}")
conn = ldap3.Connection(
server,
user=userdn,
password=password,
auto_bind=auto_bind,
)
except LDAPBindError as e:
self.log.warning(
"Failed to bind {userdn}\n{e_type}: {e_msg}".format(
userdn=userdn,
e_type=e.__class__.__name__,
e_msg=e.args[0] if e.args else "",
)
)
return None
else:
self.log.debug(f"Successfully bound {userdn}")
return conn

def get_user_attributes(self, conn, userdn):
attrs = {}
Expand Down Expand Up @@ -460,28 +481,17 @@ async def authenticate(self, handler, data):
if not bind_dn_template:
bind_dn_template = [resolved_dn]

is_bound = False
# bind to ldap user
conn = None
for dn in bind_dn_template:
# DN's attribute values should be escaped with escape_rdn to respect
# https://datatracker.ietf.org/doc/html/rfc4514#section-2.4
userdn = dn.format(username=escape_rdn(username))
self.log.debug(f"Attempting to bind {username} with {userdn}")
msg = "Status of user bind {username} with {userdn} : {is_bound}"
try:
conn = self.get_connection(userdn, password)
except ldap3.core.exceptions.LDAPBindError as exc:
is_bound = False
msg += "\n{exc_type}: {exc_msg}".format(
exc_type=exc.__class__.__name__,
exc_msg=exc.args[0] if exc.args else "",
)
else:
is_bound = True if conn.bound else conn.bind()
msg = msg.format(username=username, userdn=userdn, is_bound=is_bound)
self.log.debug(msg)
if is_bound:
conn = self.get_connection(userdn, password)
if conn:
break

if not is_bound:
self.log.warning(f"Invalid password for user '{username}'")
if not conn:
self.log.warning(f"Failed to bind user '{username}' to a LDAP user.")
return None

if self.search_filter:
Expand Down

0 comments on commit a57aaf8

Please sign in to comment.