website_membership_signup_required: fix reset_password form

This commit is contained in:
Luis 2026-07-27 11:25:19 +02:00
parent c439607ce1
commit 40d7cc8772
3 changed files with 48 additions and 3 deletions

View file

@ -3,7 +3,7 @@
{ # noqa: B018 { # noqa: B018
"name": "Website Membership Signup Required", "name": "Website Membership Signup Required",
"version": "18.0.1.0.0", "version": "18.0.1.0.1",
"category": "Website/Sale", "category": "Website/Sale",
"summary": "Force a clean /web/signup step before adding membership " "summary": "Force a clean /web/signup step before adding membership "
"products to cart on a b2b website (EMES-style flow).", "products to cart on a b2b website (EMES-style flow).",

View file

@ -32,7 +32,18 @@ class AuthSignupHomeMembership(AuthSignupHome):
# it as `qcontext['error']` which the template renders as a red # it as `qcontext['error']` which the template renders as a red
# alert above the form, so no extra plumbing is needed). The browser # alert above the form, so no extra plumbing is needed). The browser
# also enforces it client-side via `required="required"`. # also enforces it client-side via `required="required"`.
if qcontext.get("accepted_terms") != "accepted": #
# `_prepare_signup_values` is also invoked by the core during the
# password reset flow (`/web/reset_password`). That form does NOT
# render the terms checkbox, so we must skip the validation there.
# Existing users resetting their password already accepted the terms
# when they signed up. We detect the reset flow primarily by the
# request path and secondarily by the qcontext flag set by core.
is_reset_password = (
request.httprequest.path == "/web/reset_password"
or qcontext.get("reset_password")
)
if not is_reset_password and qcontext.get("accepted_terms") != "accepted":
raise UserError(_("You must accept the terms and conditions.")) raise UserError(_("You must accept the terms and conditions."))
# partner_firstname: the signup form collects `firstname` / `lastname` # partner_firstname: the signup form collects `firstname` / `lastname`
# separately. We inject both into the signup values so the resulting # separately. We inject both into the signup values so the resulting

View file

@ -137,7 +137,6 @@ class TestMembershipSignup(TransactionCase):
"""Signup must be rejected if the legal terms checkbox is not """Signup must be rejected if the legal terms checkbox is not
checked (`accepted_terms` missing or not 'accepted'). checked (`accepted_terms` missing or not 'accepted').
""" """
import unittest
controller = AuthSignupHome() controller = AuthSignupHome()
base_qcontext = { base_qcontext = {
"login": "no.terms@example.com", "login": "no.terms@example.com",
@ -161,6 +160,41 @@ class TestMembershipSignup(TransactionCase):
values = controller._prepare_signup_values(qcontext) values = controller._prepare_signup_values(qcontext)
self.assertEqual(values.get("firstname"), "No") self.assertEqual(values.get("firstname"), "No")
def test_07c_prepare_signup_values_reset_password_allows_missing_terms(self):
"""Password reset (`/web/reset_password`) reuses core's
`_prepare_signup_values`. The reset form intentionally does not
include the legal terms checkbox, so our override must not reject
the submission when `accepted_terms` is absent.
"""
controller = AuthSignupHome()
base_qcontext = {
"login": "reset.me@example.com",
"password": "newverystrongpwd",
"confirm_password": "newverystrongpwd",
"firstname": "Reset",
"lastname": "User",
"token": "dummy-reset-token",
}
# Missing `accepted_terms` on the reset path must NOT raise.
with MockRequest(
self.env, website=self.website, path="/web/reset_password"
) as mock_request:
# The helper accepts `path=` but the underlying httprequest mock
# also exposes the path attribute; double-check alignment.
self.assertEqual(mock_request.httprequest.path, "/web/reset_password")
values = controller._prepare_signup_values(dict(base_qcontext))
self.assertEqual(values.get("firstname"), "Reset")
self.assertEqual(values.get("lastname"), "User")
self.assertIn("name", values)
# Same protection when the qcontext exposes the core reset flag.
qcontext_flag = {
key: value for key, value in base_qcontext.items() if key != "token"
}
qcontext_flag["reset_password"] = True
with MockRequest(self.env, website=self.website, path="/web/signup"):
values = controller._prepare_signup_values(qcontext_flag)
self.assertEqual(values.get("firstname"), "Reset")
# -- RF-08 ------------------------------------------------------------- # -- RF-08 -------------------------------------------------------------
def test_08_backend_sale_order_with_membership_product_without_user(self): def test_08_backend_sale_order_with_membership_product_without_user(self):