test_connection_tests_use_the_form.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. """The LDAP and SMTP "Test" buttons test what the form holds, not only what was saved.
  2. A password left empty in the form keeps the saved one, which still only goes
  3. to the server it was entered for.
  4. """
  5. from unittest.mock import patch
  6. import pytest
  7. from httpx import AsyncClient
  8. LDAP_SAVED = {
  9. "ldap_server_url": "ldaps://ldap.example.com",
  10. "ldap_bind_dn": "cn=svc,dc=example,dc=com",
  11. "ldap_bind_password": "saved-bind-secret",
  12. "ldap_search_base": "dc=example,dc=com",
  13. "ldap_security": "ldaps",
  14. }
  15. SMTP_SAVED = {
  16. "smtp_host": "smtp.example.com",
  17. "smtp_port": 587,
  18. "smtp_username": "mailer@example.com",
  19. "smtp_password": "saved-smtp-secret",
  20. "smtp_security": "starttls",
  21. "smtp_auth_enabled": True,
  22. "smtp_from_email": "noreply@example.com",
  23. }
  24. async def _ldap_test(async_client: AsyncClient, body: dict | None):
  25. tested = []
  26. def fake_test(config):
  27. tested.append(config)
  28. return True, "ok"
  29. with patch("backend.app.services.ldap_service.test_ldap_connection", side_effect=fake_test):
  30. if body is None:
  31. response = await async_client.post("/api/v1/auth/ldap/test")
  32. else:
  33. response = await async_client.post("/api/v1/auth/ldap/test", json=body)
  34. assert response.status_code == 200, response.text
  35. return response.json(), tested
  36. @pytest.fixture
  37. async def ldap_saved(async_client: AsyncClient):
  38. response = await async_client.put("/api/v1/settings/", json=LDAP_SAVED)
  39. assert response.status_code == 200, response.text
  40. class TestLdap:
  41. @pytest.mark.asyncio
  42. async def test_tests_the_form_values(self, async_client, ldap_saved):
  43. result, tested = await _ldap_test(
  44. async_client,
  45. {
  46. **LDAP_SAVED,
  47. "ldap_bind_dn": "cn=other,dc=example,dc=com",
  48. "ldap_bind_password": "typed-secret",
  49. "ldap_search_base": "ou=people,dc=example,dc=com",
  50. },
  51. )
  52. assert result["success"] is True
  53. assert tested[0].bind_dn == "cn=other,dc=example,dc=com"
  54. assert tested[0].bind_password == "typed-secret"
  55. assert tested[0].search_base == "ou=people,dc=example,dc=com"
  56. @pytest.mark.asyncio
  57. async def test_an_empty_password_keeps_the_saved_one_for_the_same_server(self, async_client, ldap_saved):
  58. _, tested = await _ldap_test(async_client, {**LDAP_SAVED, "ldap_bind_password": ""})
  59. assert tested[0].bind_password == "saved-bind-secret"
  60. @pytest.mark.asyncio
  61. @pytest.mark.parametrize(
  62. "changed",
  63. [{"ldap_server_url": "ldaps://elsewhere.example.net"}, {"ldap_security": "starttls"}],
  64. )
  65. async def test_the_saved_password_never_goes_to_another_server(self, async_client, ldap_saved, changed):
  66. result, tested = await _ldap_test(async_client, {**LDAP_SAVED, "ldap_bind_password": "", **changed})
  67. assert result["success"] is False
  68. assert "again" in result["message"]
  69. assert tested == []
  70. @pytest.mark.asyncio
  71. async def test_without_a_body_it_tests_the_saved_settings(self, async_client, ldap_saved):
  72. _, tested = await _ldap_test(async_client, None)
  73. assert tested[0].server_url == "ldaps://ldap.example.com"
  74. assert tested[0].bind_password == "saved-bind-secret"
  75. @pytest.mark.asyncio
  76. async def test_a_saved_ca_certificate_is_used_while_ldap_is_off(self, async_client, db_session, ldap_saved):
  77. from backend.app.models.settings import Settings
  78. db_session.add(Settings(key="ldap_ca_cert_path", value="/certs/ca.pem"))
  79. await db_session.commit()
  80. _, tested = await _ldap_test(async_client, {**LDAP_SAVED, "ldap_bind_password": ""})
  81. assert tested[0].ca_cert_path == "/certs/ca.pem"
  82. @pytest.mark.asyncio
  83. async def test_ldap_unset_security_is_starttls_as_when_saving(async_client, db_session):
  84. """Settings saved without a security row: the form's StartTLS is no change of destination."""
  85. from backend.app.models.settings import Settings
  86. for key, value in {**LDAP_SAVED, "ldap_server_url": "ldap://ldap.example.com"}.items():
  87. if key != "ldap_security":
  88. db_session.add(Settings(key=key, value=value))
  89. await db_session.commit()
  90. result, tested = await _ldap_test(
  91. async_client,
  92. {
  93. **LDAP_SAVED,
  94. "ldap_server_url": "ldap://ldap.example.com",
  95. "ldap_security": "starttls",
  96. "ldap_bind_password": "",
  97. },
  98. )
  99. assert result["success"] is True, result
  100. assert tested[0].bind_password == "saved-bind-secret"
  101. async def _smtp_test(async_client: AsyncClient, smtp: dict | None):
  102. body: dict = {"test_recipient": "someone@example.com"}
  103. if smtp is not None:
  104. body["smtp"] = smtp
  105. with patch("backend.app.api.routes.auth.send_email") as send:
  106. response = await async_client.post("/api/v1/auth/smtp/test", json=body)
  107. assert response.status_code == 200, response.text
  108. return response.json(), send
  109. @pytest.fixture
  110. async def smtp_saved(async_client: AsyncClient):
  111. response = await async_client.post("/api/v1/auth/smtp", json=SMTP_SAVED)
  112. assert response.status_code == 200, response.text
  113. class TestSmtp:
  114. @pytest.mark.asyncio
  115. async def test_tests_the_form_values(self, async_client, smtp_saved):
  116. result, send = await _smtp_test(
  117. async_client, {**SMTP_SAVED, "smtp_from_email": "typed@example.com", "smtp_password": "typed-secret"}
  118. )
  119. assert result["success"] is True
  120. used = send.call_args.kwargs["smtp_settings"]
  121. assert used.smtp_from_email == "typed@example.com"
  122. assert used.smtp_password == "typed-secret"
  123. @pytest.mark.asyncio
  124. async def test_an_empty_password_keeps_the_saved_one_for_the_same_server(self, async_client, smtp_saved):
  125. _, send = await _smtp_test(async_client, {**SMTP_SAVED, "smtp_password": ""})
  126. assert send.call_args.kwargs["smtp_settings"].smtp_password == "saved-smtp-secret"
  127. @pytest.mark.asyncio
  128. @pytest.mark.parametrize(
  129. "changed", [{"smtp_host": "mail.elsewhere.example.net"}, {"smtp_port": 2525}, {"smtp_security": "ssl"}]
  130. )
  131. async def test_the_saved_password_never_goes_to_another_server(self, async_client, smtp_saved, changed):
  132. result, send = await _smtp_test(async_client, {**SMTP_SAVED, "smtp_password": "", **changed})
  133. assert result["success"] is False
  134. assert "again" in result["message"]
  135. send.assert_not_called()
  136. @pytest.mark.asyncio
  137. async def test_without_the_form_it_tests_the_saved_settings(self, async_client, smtp_saved):
  138. _, send = await _smtp_test(async_client, None)
  139. assert send.call_args.kwargs["smtp_settings"].smtp_host == "smtp.example.com"
  140. @pytest.mark.asyncio
  141. async def test_the_form_works_before_anything_was_saved(self, async_client):
  142. result, send = await _smtp_test(async_client, SMTP_SAVED)
  143. assert result["success"] is True
  144. assert send.call_args.kwargs["smtp_settings"].smtp_password == "saved-smtp-secret"