diff --git a/frontend/src/lib/components/UserForgotPassword.svelte b/frontend/src/lib/components/UserForgotPassword.svelte index 1844a88a..131b72f2 100644 --- a/frontend/src/lib/components/UserForgotPassword.svelte +++ b/frontend/src/lib/components/UserForgotPassword.svelte @@ -1,20 +1,17 @@ + + +{#if showAlert === true} + { + showAlert = false; + }}/> +{:else} + {#if success === true} +
+ +
+ {$_('forgotPw.successReset')} +
+
+ + {:else} + + + {$_('forgotPw.resetHeading')} + +
+
+ + + + +
+
+ +
+
+
+ {/if} +{/if} diff --git a/mondey_backend/src/mondey_backend/users.py b/mondey_backend/src/mondey_backend/users.py index 472f3f11..e3ac24ed 100644 --- a/mondey_backend/src/mondey_backend/users.py +++ b/mondey_backend/src/mondey_backend/users.py @@ -39,6 +39,18 @@ def send_email_validation_link(email: str, token: str) -> None: s.send_message(msg) +def send_reset_password_link(email: str, token: str) -> None: + msg = EmailMessage() + msg["From"] = "no-reply@mondey.lkeegan.dev" + msg["To"] = email + msg["Subject"] = "MONDEY Passwort zurücksetzen" + msg.set_content( + f"Bitte klicken Sie hier, um Ihr MONDEY Passwort zurückzusetzen:\n\nhttps://mondey.lkeegan.dev/resetPassword/{token}\n\n-----\n\nPlease click here to reset your MONDEY password:\n\nhttps://mondey.lkeegan.dev/resetPassword/{token}" + ) + with smtplib.SMTP(app_settings.SMTP_HOST) as s: + s.send_message(msg) + + class UserManager(IntegerIDMixin, BaseUserManager[User, int]): reset_password_token_secret = app_settings.SECRET verification_token_secret = app_settings.SECRET @@ -60,7 +72,8 @@ async def on_after_register(self, user: User, request: Request | None = None): async def on_after_forgot_password( self, user: User, token: str, request: Request | None = None ): - print(f"User {user.id} has forgot their password. Reset token: {token}") + logging.info(f"User {user.id} has forgot their password. Reset token: {token}") + send_reset_password_link(user.email, token) async def on_after_request_verify( self, user: User, token: str, request: Request | None = None diff --git a/mondey_backend/tests/routers/test_auth.py b/mondey_backend/tests/routers/test_auth.py index fedc1e50..4a0b037f 100644 --- a/mondey_backend/tests/routers/test_auth.py +++ b/mondey_backend/tests/routers/test_auth.py @@ -73,3 +73,62 @@ def test_register_new_user_valid_research_code( new_user = admin_client.get("/admin/users/").json()[-1] assert new_user["email"] == email assert new_user["research_group_id"] == 123451 + + +def test_user_reset_password(user_client: TestClient, smtp_mock: SMTPMock): + assert smtp_mock.last_message is None + email = "user@mondey.de" + response = user_client.post("/auth/forgot-password", json={"email": email}) + assert response.status_code == 202 + + msg = smtp_mock.last_message + assert msg is not None + assert msg.get("To") == email + token = msg.get_content().split("\n\n")[1].rsplit("/")[-1] + new_password = "new_password" + response = user_client.post( + "/auth/reset-password", json={"token": token, "password": new_password} + ) + assert response.status_code == 200 + + +def test_user_reset_password_invalid_token( + user_client: TestClient, smtp_mock: SMTPMock +): + assert smtp_mock.last_message is None + email = "user@mondey.de" + response = user_client.post("/auth/forgot-password", json={"email": email}) + assert response.status_code == 202 + + msg = smtp_mock.last_message + assert msg is not None + assert msg.get("To") == email + token = msg.get_content().split("\n\n")[1].rsplit("/")[-1] + "invalid" + new_password = "new_password" + response = user_client.post( + "/auth/reset-password", json={"token": token, "password": new_password} + ) + assert response.status_code == 400 + + +def test_user_forgot_password( + user_client: TestClient, active_user, smtp_mock: SMTPMock +): + assert smtp_mock.last_message is None + response = user_client.post( + "/auth/forgot-password", json={"email": active_user.email} + ) + assert response.status_code == 202 + + +def test_user_forgot_password_invalid_email( + user_client: TestClient, smtp_mock: SMTPMock +): + assert smtp_mock.last_message is None + email = "invalid-email" + response = user_client.post("/auth/forgot-password", json={"email": email}) + assert ( + response.json()["detail"][0]["msg"] + == "value is not a valid email address: An email address must have an @-sign." + ) + assert response.json()["detail"][0]["type"] == "value_error" diff --git a/mondey_backend/tests/utils/test_statistics.py b/mondey_backend/tests/utils/test_statistics.py index 1efedef9..a0ca079e 100644 --- a/mondey_backend/tests/utils/test_statistics.py +++ b/mondey_backend/tests/utils/test_statistics.py @@ -89,7 +89,6 @@ def test_online_statistics_computation_too_little_data(): def test_get_score_statistics_by_age(session): answers = session.exec(select(MilestoneAnswer)).all() - print(answers) # which answers we choose here is arbitrary for testing, we just need to make sure it's fixed and not empty child_ages = { 1: 5, @@ -213,12 +212,6 @@ def test_calculate_milestone_statistics_by_age(statistics_session): # we have nothing new for everything else for age in range(0, len(mscore.scores)): - print( - age, - mscore.scores[age].count, - mscore.scores[age].avg_score, - mscore.scores[age].stddev_score, - ) if age != 8: assert mscore.scores[age].count == 12 avg = 0 if age < 5 else min(1 * age - 5, 3)