diff --git a/authentik/sources/ldap/auth.py b/authentik/sources/ldap/auth.py index 5cc21f991..508aacb3e 100644 --- a/authentik/sources/ldap/auth.py +++ b/authentik/sources/ldap/auth.py @@ -57,13 +57,13 @@ class LDAPBackend(InbuiltBackend): # Try to bind as new user LOGGER.debug("Attempting to bind as user", user=user) try: - temp_connection = source.connection( + # source.connection also attempts to bind + source.connection( connection_kwargs={ "user": user.attributes.get(LDAP_DISTINGUISHED_NAME), "password": password, } ) - temp_connection.bind() return user except LDAPInvalidCredentialsResult as exc: LOGGER.debug("invalid LDAP credentials", user=user, exc=exc) diff --git a/authentik/sources/ldap/models.py b/authentik/sources/ldap/models.py index 4cbddd582..2b4acbedf 100644 --- a/authentik/sources/ldap/models.py +++ b/authentik/sources/ldap/models.py @@ -145,7 +145,9 @@ class LDAPSource(Source): if self.start_tls: connection.start_tls(read_server_info=False) try: - connection.bind() + successful = connection.bind() + if successful: + return connection except LDAPSchemaError as exc: # Schema error, so try connecting without schema info # See https://github.com/goauthentik/authentik/issues/4590 @@ -153,7 +155,7 @@ class LDAPSource(Source): raise exc server_kwargs["get_info"] = NONE return self.connection(server_kwargs, connection_kwargs) - return connection + return RuntimeError("Failed to bind") class Meta: verbose_name = _("LDAP Source") diff --git a/authentik/sources/ldap/tests/test_auth.py b/authentik/sources/ldap/tests/test_auth.py index 3ba6ae386..715edc139 100644 --- a/authentik/sources/ldap/tests/test_auth.py +++ b/authentik/sources/ldap/tests/test_auth.py @@ -29,6 +29,37 @@ class LDAPSyncTests(TestCase): additional_group_dn="ou=groups", ) + def test_auth_direct_user_ad(self): + """Test direct auth""" + self.source.property_mappings.set( + LDAPPropertyMapping.objects.filter( + Q(managed__startswith="goauthentik.io/sources/ldap/default-") + | Q(managed__startswith="goauthentik.io/sources/ldap/ms-") + ) + ) + raw_conn = mock_ad_connection(LDAP_PASSWORD) + bind_mock = Mock(wraps=raw_conn.bind) + raw_conn.bind = bind_mock + connection = MagicMock(return_value=raw_conn) + with patch("authentik.sources.ldap.models.LDAPSource.connection", connection): + user_sync = UserLDAPSynchronizer(self.source) + user_sync.sync() + + user = User.objects.get(username="user0_sn") + # auth_user_by_bind = Mock(return_value=user) + backend = LDAPBackend() + self.assertEqual( + backend.authenticate(None, username="user0_sn", password=LDAP_PASSWORD), + user, + ) + connection.assert_called_with( + connection_kwargs={ + "user": "cn=user0,ou=users,dc=goauthentik,dc=io", + "password": LDAP_PASSWORD, + } + ) + bind_mock.assert_not_called() + def test_auth_synced_user_ad(self): """Test Cached auth""" self.source.property_mappings.set(