test_user_email_clear.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. """Editing a user can remove their email: an empty email clears it.
  2. Left out or null, the email stays as it is. With advanced authentication on,
  3. a user needs an email (password resets go there), so it can't be removed.
  4. """
  5. import pytest
  6. from httpx import AsyncClient
  7. from sqlalchemy import select
  8. from backend.app.models.settings import Settings
  9. from backend.app.models.user import User
  10. async def _user(db_session, email: str | None = "mia@example.invalid") -> User:
  11. from backend.app.core.auth import get_password_hash
  12. user = User(username="mia", password_hash=get_password_hash("Whatever1!"), email=email, role="user", is_active=True)
  13. db_session.add(user)
  14. await db_session.commit()
  15. return user
  16. async def _email(db_session, user_id: int) -> str | None:
  17. db_session.expire_all()
  18. return (await db_session.execute(select(User.email).where(User.id == user_id))).scalar_one()
  19. @pytest.mark.asyncio
  20. @pytest.mark.parametrize("email", ["", " "])
  21. async def test_an_empty_email_clears_it(async_client: AsyncClient, db_session, email):
  22. user = await _user(db_session)
  23. response = await async_client.patch(f"/api/v1/users/{user.id}", json={"email": email})
  24. assert response.status_code == 200, response.text
  25. assert response.json()["email"] is None
  26. assert await _email(db_session, user.id) is None
  27. @pytest.mark.asyncio
  28. @pytest.mark.parametrize("body", [{}, {"email": None}, {"role": "user"}])
  29. async def test_left_out_or_null_keeps_it(async_client: AsyncClient, db_session, body):
  30. user = await _user(db_session)
  31. response = await async_client.patch(f"/api/v1/users/{user.id}", json=body)
  32. assert response.status_code == 200, response.text
  33. assert await _email(db_session, user.id) == "mia@example.invalid"
  34. @pytest.mark.asyncio
  35. async def test_two_users_without_email_are_no_duplicate(async_client: AsyncClient, db_session):
  36. from backend.app.core.auth import get_password_hash
  37. db_session.add(User(username="other", password_hash=get_password_hash("Whatever1!"), email=None, role="user"))
  38. user = await _user(db_session)
  39. response = await async_client.patch(f"/api/v1/users/{user.id}", json={"email": ""})
  40. assert response.status_code == 200, response.text
  41. @pytest.mark.asyncio
  42. async def test_refused_while_advanced_authentication_is_on(async_client: AsyncClient, db_session):
  43. user = await _user(db_session)
  44. db_session.add(Settings(key="advanced_auth_enabled", value="true"))
  45. await db_session.commit()
  46. response = await async_client.patch(f"/api/v1/users/{user.id}", json={"email": ""})
  47. assert response.status_code == 400
  48. assert "advanced authentication" in response.json()["detail"]
  49. assert await _email(db_session, user.id) == "mia@example.invalid"
  50. @pytest.mark.asyncio
  51. async def test_a_user_without_email_can_still_be_edited_with_advanced_authentication(
  52. async_client: AsyncClient, db_session
  53. ):
  54. user = await _user(db_session, email=None)
  55. db_session.add(Settings(key="advanced_auth_enabled", value="true"))
  56. await db_session.commit()
  57. response = await async_client.patch(f"/api/v1/users/{user.id}", json={"email": "", "role": "user"})
  58. assert response.status_code == 200, response.text
  59. assert await _email(db_session, user.id) is None
  60. @pytest.mark.asyncio
  61. async def test_refused_while_the_user_signs_in_with_an_email_code(async_client: AsyncClient, db_session):
  62. """Email 2FA needs SMTP, not advanced authentication: the code goes to this address."""
  63. user = await _user(db_session)
  64. db_session.add(Settings(key=f"user_{user.id}_email_2fa_enabled", value="true"))
  65. await db_session.commit()
  66. response = await async_client.patch(f"/api/v1/users/{user.id}", json={"email": ""})
  67. assert response.status_code == 400
  68. assert "email" in response.json()["detail"].lower()
  69. assert await _email(db_session, user.id) == "mia@example.invalid"