Files
wizarr/tests/test_ldap_client.py

249 lines
8.6 KiB
Python

from unittest.mock import MagicMock, patch
import pytest
from app.models import LDAPConfiguration
from app.services.ldap.client import LDAPClient
@pytest.fixture
def ldap_config():
from app.services.ldap.encryption import encrypt_credential
config = LDAPConfiguration()
config.server_url = "ldap://ldap.example.com:389"
config.use_tls = True
config.verify_cert = True
config.service_account_dn = "cn=wizarr,ou=people,dc=example,dc=com"
# Use a properly encrypted password for testing
config.service_account_password_encrypted = encrypt_credential("test_password")
config.user_base_dn = "ou=people,dc=example,dc=com"
config.user_search_filter = "(uid={username})"
config.username_attribute = "uid"
config.email_attribute = "mail"
config.user_object_class = "inetOrgPerson"
config.group_base_dn = "ou=groups,dc=example,dc=com"
config.group_object_class = "groupOfUniqueNames"
config.group_member_attribute = "uniqueMember"
return config
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_test_connection_success(mock_server, mock_connection, ldap_config):
"""Test successful LDAP connection test."""
mock_conn = MagicMock()
mock_connection.return_value = mock_conn
client = LDAPClient(ldap_config)
success, message = client.test_connection()
assert success is True
assert message == "Connection successful"
mock_conn.unbind.assert_called_once()
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_test_connection_bind_error(mock_server, mock_connection, ldap_config):
from ldap3.core.exceptions import LDAPBindError
mock_connection.side_effect = LDAPBindError("Invalid credentials")
client = LDAPClient(ldap_config)
success, message = client.test_connection()
assert success is False
assert "Authentication failed" in message
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_authenticate_user_success(mock_server, mock_connection, ldap_config):
# Mock service account connection for DN lookup
mock_service_conn = MagicMock()
mock_entry = MagicMock()
mock_entry.entry_dn = "uid=testuser,ou=people,dc=example,dc=com"
mock_service_conn.entries = [mock_entry]
# Mock user connection for authentication
mock_user_conn = MagicMock()
mock_user_entry = MagicMock()
mock_user_entry.entry_dn = "uid=testuser,ou=people,dc=example,dc=com"
mock_user_entry.uid.value = "testuser"
mock_user_entry.mail.value = "test@example.com"
mock_user_conn.entries = [mock_user_entry]
# Return service conn first, then user conn
mock_connection.side_effect = [mock_service_conn, mock_user_conn]
client = LDAPClient(ldap_config)
success, attrs = client.authenticate_user("testuser", "userpassword")
assert success is True
assert "uid" in attrs
assert attrs["uid"] == "testuser"
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_create_user_success(mock_server, mock_connection, ldap_config):
mock_conn = MagicMock()
mock_conn.add.return_value = True
mock_conn.extend.standard.modify_password.return_value = True
mock_conn.search.return_value = False
mock_conn.entries = []
mock_connection.return_value = mock_conn
client = LDAPClient(ldap_config)
success, result = client.create_user(
username="newuser",
email="newuser@example.com",
password="password123",
)
assert success is True
assert "uid=newuser,ou=people,dc=example,dc=com" in result
mock_conn.add.assert_called_once()
mock_conn.extend.standard.modify_password.assert_called_once()
mock_conn.unbind.assert_called_once()
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_search_groups(mock_server, mock_connection, ldap_config):
mock_conn = MagicMock()
mock_conn.search.return_value = True
# Mock group entries
mock_entry1 = MagicMock()
mock_entry1.entry_dn = "cn=users,ou=groups,dc=example,dc=com"
mock_entry1.cn.value = "users"
mock_entry2 = MagicMock()
mock_entry2.entry_dn = "cn=admins,ou=groups,dc=example,dc=com"
mock_entry2.cn.value = "admins"
mock_conn.entries = [mock_entry1, mock_entry2]
mock_connection.return_value = mock_conn
client = LDAPClient(ldap_config)
groups = client.search_groups()
assert len(groups) == 2
assert groups[0]["cn"] == "users"
assert groups[0]["description"] is None
assert groups[1]["cn"] == "admins"
assert groups[1]["description"] is None
mock_conn.search.assert_called_once()
mock_conn.unbind.assert_called_once()
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_delete_user_success(mock_server, mock_connection, ldap_config):
"""Test successful LDAP user deletion."""
mock_conn = MagicMock()
mock_conn.delete.return_value = True
mock_connection.return_value = mock_conn
client = LDAPClient(ldap_config)
user_dn = "uid=testuser,ou=people,dc=example,dc=com"
success, message = client.delete_user(user_dn)
assert success is True
assert message == "User deleted successfully"
mock_conn.delete.assert_called_once_with(user_dn)
mock_conn.unbind.assert_called_once()
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_delete_user_failure(mock_server, mock_connection, ldap_config):
"""Test failed LDAP user deletion."""
mock_conn = MagicMock()
mock_conn.delete.return_value = False
mock_conn.result = {"description": "No such object"}
mock_connection.return_value = mock_conn
client = LDAPClient(ldap_config)
user_dn = "uid=nonexistent,ou=people,dc=example,dc=com"
success, message = client.delete_user(user_dn)
assert success is False
assert "Failed to delete LDAP user" in message
mock_conn.unbind.assert_called_once()
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_delete_user_missing_dn(mock_server, mock_connection, ldap_config):
"""Test LDAP user deletion with missing DN."""
client = LDAPClient(ldap_config)
success, message = client.delete_user("")
assert success is False
assert message == "User DN is required"
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_change_password_success(mock_server, mock_connection, ldap_config):
"""Test successful LDAP password change."""
mock_conn = MagicMock()
mock_conn.extend.standard.modify_password.return_value = True
mock_connection.return_value = mock_conn
client = LDAPClient(ldap_config)
user_dn = "uid=testuser,ou=people,dc=example,dc=com"
success, message = client.change_password(user_dn, "newpassword123")
assert success is True
assert message == "Password changed successfully"
mock_conn.extend.standard.modify_password.assert_called_once_with(
user_dn, None, "newpassword123"
)
mock_conn.unbind.assert_called_once()
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_change_password_failure(mock_server, mock_connection, ldap_config):
"""Test failed LDAP password change."""
mock_conn = MagicMock()
mock_conn.extend.standard.modify_password.return_value = False
mock_conn.result = {"description": "Insufficient access rights"}
mock_connection.return_value = mock_conn
client = LDAPClient(ldap_config)
user_dn = "uid=testuser,ou=people,dc=example,dc=com"
success, message = client.change_password(user_dn, "newpassword123")
assert success is False
assert "Failed to change password" in message
assert "lldap_password_manager" in message
mock_conn.unbind.assert_called_once()
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_change_password_missing_dn(mock_server, mock_connection, ldap_config):
"""Test LDAP password change with missing DN."""
client = LDAPClient(ldap_config)
success, message = client.change_password("", "newpassword123")
assert success is False
assert message == "User DN is required"
@patch("app.services.ldap.client.Connection")
@patch("app.services.ldap.client.Server")
def test_change_password_missing_password(mock_server, mock_connection, ldap_config):
"""Test LDAP password change with missing password."""
client = LDAPClient(ldap_config)
user_dn = "uid=testuser,ou=people,dc=example,dc=com"
success, message = client.change_password(user_dn, "")
assert success is False
assert message == "New password is required"