remove next query string from authentication logic.

This commit is contained in:
Russell Ballestrini 2024-12-14 21:57:08 +00:00
parent c9aa05574e
commit eb5f05f2a0
8 changed files with 45 additions and 86 deletions

View file

@ -41,7 +41,7 @@ app.root_domain = blanka.foxhop.net
app.root_domain_owner_email = russell.ballestrini@gmail.com
app.root_url = http://blanka.foxhop.net:6501
#app.email.relay = localhost:8025
app.email.relay = localhost:8025
# TODO: these values should be moved to environment variables & we should create an
# example app.env environment file.

View file

@ -249,7 +249,7 @@ def send_invite_email(request, to_email, user, shop):
subject = f"You have been invited to {shop.name}"
join_link = request.route_url(
"join-or-log-in",
_query={"email": to_email, "next": request.route_url("actions_new")},
_query={"email": to_email},
)
message_text = INVITE_1_TEXT.format(user.email, shop.name, join_link)
message_html = INVITE_1_HTML.format(subject, user.email, shop.name, join_link)

View file

@ -27,11 +27,6 @@
required
autofocus />
<input
name = "next"
type = "hidden"
value = "{{ next }}" />
<br/>
<br/>

View file

@ -12,6 +12,7 @@
{% for location in locations %}
<li>
<a href="{{ request.route_url('shop_location_switch', shop_id=shop.id, location_id=location.id, _query={'next': request.params.get('next', request.url)}) }}">{{ location.name }}</a>
{% if location == request.shop_location %}(active){% endif %}
{% if request.user in request.shop.owners %}
<a href="{{ request.route_url('shop_location_edit', shop_id=shop.id, location_id=location.id) }}" class="button">&#9881; edit</a>
{% endif %}

View file

@ -2,41 +2,25 @@
{% block content -%}
<section class="one-column">
<section class="log-in-form well">
<h2>Verification Code</h2>
<b>Please enter code to log in.<b>
<br>
<br>
<form method="post" action="/verification-challenge">
<input
name = "email"
type = "hidden"
id = "email_input"
class = "common-text-input"
value = "{{ email }}"
placeholder = "Your Email Address" />
<h2>Verification Code</h2>
<b>Please enter code to log in.</b>
<br>
<br>
<form method="post" action="/verification-challenge">
<input
name = "raw-otp"
type = "number"
id = "raw-otp"
class = "common-text-input"
tabindex = "1"
value = "{% if raw_otp %}{{ raw_otp }}{% endif %}"
value = "{% if raw_otp %}{{ raw_otp }}{% endif %}"
placeholder = "6 digit challenge verification code"
required
autofocus />
<input
name = "next"
type = "hidden"
value = "{{ next }}" />
<br/>
<br/>
<input type="submit" name="submit" id="submit" value="verify code" required />
</form>
<br/>
<br/>
<input type="submit" name="submit" id="submit" value="Verify Code" />
</form>
<br/>
</section>
</section>

View file

@ -198,11 +198,15 @@ class AuthenticatedFunctionalTests(FunctionalTests):
super(AuthenticatedFunctionalTests, self).tearDown()
def log_in_user(self, user_creds):
# log in user.
# Set the email in the session before posting to the verification challenge
self.testapp.post("/join-or-log-in", {"email": user_creds[0]})
# Simulate entering the OTP
res_login = self.testapp.post(
"/verification-challenge?email={}&raw-otp={}&submit".format(*user_creds)
"/verification-challenge", {"raw-otp": user_creds[1], "submit": True}
)
# attach csrf to class.
# Attach csrf to class if needed
res_csrf = self.testapp.get("/")
# self.csrf = res_csrf.form.fields["csrf_token"][0].value
return res_login

View file

@ -18,12 +18,11 @@ def user_required(
def inner(request):
if request.user and request.user.authenticated:
return fn(request)
# Flash message
request.session.flash((flash_msg, flash_level))
next_url = request.url
# Redirect to the login route
if redirect_to_route_name:
return HTTPFound(
request.route_url(redirect_to_route_name, _query={"next": next_url})
)
return HTTPFound(request.route_url(redirect_to_route_name))
return HTTPFound(get_referer_or_home(request))
return inner

View file

@ -34,15 +34,10 @@ def join_or_log_in(request):
address & to authenticate the device displaying the challenge input field.
"""
_email_regex = re.compile("^[^@]+@[^@]+\.[^.@]+$")
# get the raw OTP (one-time-password) from posted parameters.
raw_otp = request.params.get("raw-otp", "")
# get the email from posted parameters.
email = request.params.get("email", "")
if email and _email_regex.match(email) is None:
# posted email does not pass regex, set it to None.
email = ""
request.session.flash(("That email address is invalid.", "error"))
@ -50,10 +45,10 @@ def join_or_log_in(request):
return request.spam
if request.user and request.user.authenticated:
request.session.flash(("You are already authenticated!", "info"))
return HTTPFound(get_referer_or_home(request))
elif email:
# get or create a User object from the posted email.
user = get_or_create_user_by_email(request.dbsession, email)
if user.throttle_password():
@ -61,16 +56,11 @@ def join_or_log_in(request):
f"Check email for a 6 digit verification code to log in. {user.email}",
"info",
)
else:
# generate a new one-time-password and save to database
raw_otp = user.new_password()
request.dbsession.add(user)
request.dbsession.flush()
# email user the one-time-password and flash message.
send_verification_digits_to_email(request, user.email, raw_otp)
msg = (
f"Check email for a 6 digit verification code to log in. {user.email}",
"info",
@ -78,62 +68,50 @@ def join_or_log_in(request):
request.session.flash(msg)
email_encoded = urlencode(
{"email": email, "next": request.params.get("next", "")}
)
return HTTPFound(f"/verification-challenge?{email_encoded}")
# Store 'unauthed_email' in session instead of passing via query param
request.session["unauthed_email"] = email
# Redirect to verification challenge without 'unauthed_email' in query
return HTTPFound("/verification-challenge")
return {
"title": "join or log in",
"next": request.params.get("next", request.route_url("home")),
"title": "Join or Log In",
}
@view_config(route_name="verification-challenge", renderer="verification-challenge.j2")
def verification_challenge(request):
# get the raw OTP (one-time-password) from posted parameters.
raw_otp = request.params.get("raw-otp", "")
# Retrieve 'unauthed_email' from the session
unauthed_email = request.session.get("unauthed_email", "")
# get the email from posted parameters.
email = request.params.get("email", "")
if not unauthed_email:
# If 'unauthed_email' is not in session, redirect to login/join
request.session.flash(("Email is required for verification.", "error"))
return HTTPFound("/join-or-log-in")
user = None
if email:
# get or create a User object from the posted email.
user = get_or_create_user_by_email(request.dbsession, email)
user = get_or_create_user_by_email(request.dbsession, unauthed_email)
if "submit" in request.params:
raw_otp = request.params.get("raw-otp", "")
if raw_otp and user.check_password(raw_otp):
# success: the user was verified.
user.verified = True
name = ""
if user.full_name:
name = user.name
name = user.full_name or ""
msg = (f"Welcome {name}", "success")
_ = request.session.pop("unauthed_email", None)
request.session["authenticated_user_id"] = str(user.id)
request.session.flash(msg)
# merge any products in the session cart
# into request user's active cart for this shop.
# Merge carts
request.active_cart.merge_in_cart(request.session_cart)
# delete old session only cart.
request.dbsession.delete(request.session_cart)
# make session cart match user's active cart.
request.session["active_cart_id"] = str(request.active_cart.id)
# save user and user's active cart.
request.dbsession.add(user)
request.dbsession.add(request.active_cart)
request.dbsession.flush()
if request.active_cart.count > 0:
next_url = "/cart"
else:
next_url = request.params.get("next", request.route_url("home"))
return HTTPFound(next_url)
return HTTPFound(
"/cart" if request.active_cart.count > 0 else request.route_url("home")
)
else:
msg = ("Invalid Verification Code", "error")
@ -141,7 +119,5 @@ def verification_challenge(request):
return {
"title": "Please Enter Verification Code",
"email": email,
"raw_otp": raw_otp,
"next": request.params.get("next", request.route_url("home")),
"raw_otp": request.params.get("raw-otp", ""),
}