From 0a1466787aea27b36ee546bf82416edeb8f1d8db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lo=C3=AFc=20Dachary?= <ldachary@easter-eggs.com>
Date: Thu, 11 Feb 2021 16:06:44 +0100
Subject: [PATCH 4/4] ldap: bind serverctrls is not used when use_controls is
 false

---
 src/authentic2/backends/ldap_backend.py | 12 +++++---
 tests/test_ldap.py                      | 37 ++++++++++++++++++++-----
 2 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/src/authentic2/backends/ldap_backend.py b/src/authentic2/backends/ldap_backend.py
index b414de98a..63793412d 100644
--- a/src/authentic2/backends/ldap_backend.py
+++ b/src/authentic2/backends/ldap_backend.py
@@ -540,6 +540,8 @@ class LDAPBackend(object):
         'can_reset_password': False,
         # mapping from LDAP attributes to User attributes
         'user_attributes': [],
+        # https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#ldap-controls
+        'use_controls': True,
     }
     _REQUIRED = ('url', 'basedn')
     _TO_ITERABLE = ('url', 'groupsu', 'groupstaff', 'groupactive')
@@ -672,9 +674,11 @@ class LDAPBackend(object):
                         if failed:
                             continue
                         try:
-                            results = conn.simple_bind_s(authz_id, password, serverctrls=[
-                                ppolicy.PasswordPolicyControl()
-                            ])
+                            if block.get('use_controls'):
+                                serverctrls = [ppolicy.PasswordPolicyControl()]
+                            else:
+                                serverctrls = []
+                            results = conn.simple_bind_s(authz_id, password, serverctrls=serverctrls)
                             self.process_controls(request, authz_id, results[3])
                             user_login_success(authz_id)
                             if not block['connect_with_user_credentials']:
@@ -685,7 +689,7 @@ class LDAPBackend(object):
                                     raise ldap.SERVER_DOWN
                             break
                         except ldap.INVALID_CREDENTIALS as e:
-                            if len(e.args) > 0 and 'ctrls' in e.args[0]:
+                            if block.get('use_controls') and len(e.args) > 0 and 'ctrls' in e.args[0]:
                                 self.process_controls(request, authz_id, DecodeControlTuples(e.args[0]['ctrls']))
                             user_login_failure(authz_id)
                             pass
diff --git a/tests/test_ldap.py b/tests/test_ldap.py
index 014d4190c..f9d7befbe 100644
--- a/tests/test_ldap.py
+++ b/tests/test_ldap.py
@@ -951,13 +951,7 @@ pwdSafeModify: FALSE
     assert 'account is locked' in str(response.pyquery('.messages'))
 
 
-def test_authenticate_ppolicy_pwdMaxFailure(slapd_ppolicy, settings, db, caplog):
-    settings.LDAP_AUTH_SETTINGS = [{
-        'url': [slapd_ppolicy.ldap_url],
-        'basedn': u'o=ôrga',
-        'use_tls': False,
-    }]
-
+def ppolicy_authenticate_exactly_pwdMaxFailure(slapd_ppolicy, caplog):
     pwdMaxFailure = 2
     slapd_ppolicy.add_ldif('''
 dn: cn=default,ou=ppolicies,o=ôrga
@@ -987,11 +981,40 @@ pwdSafeModify: FALSE
     for _ in range(pwdMaxFailure):
         assert authenticate(username=USERNAME, password='incorrect') is None
         assert "failed to login" in caplog.text
+
+
+def test_authenticate_ppolicy_pwdMaxFailure(slapd_ppolicy, settings, db, caplog):
+    settings.LDAP_AUTH_SETTINGS = [{
+        'url': [slapd_ppolicy.ldap_url],
+        'basedn': u'o=ôrga',
+        'use_tls': False,
+    }]
+
+    ppolicy_authenticate_exactly_pwdMaxFailure(slapd_ppolicy, caplog)
     assert 'account is locked' not in caplog.text
     assert authenticate(username=USERNAME, password='incorrect') is None
     assert 'account is locked' in caplog.text
 
 
+def test_do_not_use_controls(slapd_ppolicy, settings, db, caplog):
+    """
+    Same as test_authenticate_ppolicy_pwdMaxFailure but with use_controls
+    deactivated and therefore not logging when an account is locked.
+    """
+    settings.LDAP_AUTH_SETTINGS = [{
+        'url': [slapd_ppolicy.ldap_url],
+        'basedn': u'o=ôrga',
+        'use_tls': False,
+        'use_controls': False,
+    }]
+
+    ppolicy_authenticate_exactly_pwdMaxFailure(slapd_ppolicy, caplog)
+    assert 'account is locked' not in caplog.text
+    assert authenticate(username=USERNAME, password='incorrect') is None
+    # this following line is the difference with test_authenticate_ppolicy_pwdMaxFailure
+    assert 'account is locked' not in caplog.text
+
+
 def test_authenticate_ppolicy_pwdGraceAuthnLimit(slapd_ppolicy, settings, db, caplog):
     settings.LDAP_AUTH_SETTINGS = [{
         'url': [slapd_ppolicy.ldap_url],
-- 
2.25.1

