From cc5c4114e04bde223d12c9b1ff56c6a518fad125 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 13 Nov 2024 10:58:40 -0500 Subject: [PATCH] Fixed email verify, put time limits on redis stuff. Signed-off-by: Cliff Hill --- app/main/views/index.py | 6 ++++-- app/main/views/sign_in.py | 27 +++++++++++++++++++------- app/notify_client/invite_api_client.py | 16 +++++++++------ app/notify_client/user_api_client.py | 2 +- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/app/main/views/index.py b/app/main/views/index.py index 7728cb325..28ede59d1 100644 --- a/app/main/views/index.py +++ b/app/main/views/index.py @@ -35,6 +35,8 @@ def index(): if current_user and current_user.is_authenticated: return redirect(url_for("main.choose_account")) + ttl = 24 * 60 * 60 + # make and store the state state = generate_token( str(request.remote_addr), @@ -42,12 +44,12 @@ def index(): current_app.config["DANGEROUS_SALT"], ) state_key = f"login-state-{unquote(state)}" - redis_client.set(state_key, state) + redis_client.set(state_key, state, ex=ttl) # make and store the nonce nonce = secrets.token_urlsafe() nonce_key = f"login-nonce-{unquote(nonce)}" - redis_client.set(nonce_key, nonce) + redis_client.set(nonce_key, nonce, ex=ttl) url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL") if url is not None: diff --git a/app/main/views/sign_in.py b/app/main/views/sign_in.py index d948f459e..061d30d22 100644 --- a/app/main/views/sign_in.py +++ b/app/main/views/sign_in.py @@ -108,11 +108,18 @@ def _do_login_dot_gov(): # $ pragma: no cover ) raise Exception(f"Could not login with login.gov {login_gov_error}") elif code and state: - state_key = f"login-state-{unquote(state)}" - stored_state = unquote(redis_client.get(state_key).decode("utf8")) - if state != stored_state: - current_app.logger.error(f"State Error: {state} != {stored_state}") - abort(403) + try: + state_key = f"login-state-{unquote(state)}" + stored_state = unquote(redis_client.get(state_key).decode("utf8")) + if state != stored_state: + current_app.logger.error(f"State Error: {state} != {stored_state}") + abort(403) + except AttributeError: # There is no stored state + verify_key = f"login-verify_email-{unquote(state)}" + verify_path = bool(redis_client.get(verify_key)) + if not verify_path: + current_app.logger.error("Not verify_email path, no state found.") + abort(403) # activate the user try: @@ -135,6 +142,10 @@ def _do_login_dot_gov(): # $ pragma: no cover user["email_access_validated_at"], 90 ) if not is_fresh_email: + # send email verify + ttl = 24 * 60 * 60 + verify_key = f"login-verify_email-{unquote(state)}" + redis_client.set(verify_key, state, ex=ttl) return verify_email(user, redirect_url) usr = User.from_email_address(user["email_address"]) @@ -209,17 +220,19 @@ def sign_in(): # pragma: no cover return redirect(redirect_url) return redirect(url_for("main.show_accounts_or_dashboard")) + ttl = 24 * 60 * 60 + state = generate_token( str(request.remote_addr), current_app.config["SECRET_KEY"], current_app.config["DANGEROUS_SALT"], ) state_key = f"login-state-{unquote(state)}" - redis_client.set(state_key, state) + redis_client.set(state_key, state, ex=ttl) nonce = secrets.token_urlsafe() nonce_key = f"login-nonce-{unquote(nonce)}" - redis_client.set(nonce_key, nonce) + redis_client.set(nonce_key, nonce, ex=ttl) url = os.getenv("LOGIN_DOT_GOV_INITIAL_SIGNIN_URL") # handle unit tests diff --git a/app/notify_client/invite_api_client.py b/app/notify_client/invite_api_client.py index 39cf7dbce..4ed5eee11 100644 --- a/app/notify_client/invite_api_client.py +++ b/app/notify_client/invite_api_client.py @@ -41,6 +41,8 @@ class InviteApiClient(NotifyAdminAPIClient): } data = _attach_current_user(data) + ttl = 24 * 60 * 60 + # make and store the state state = generate_token( str(request.remote_addr), @@ -48,12 +50,12 @@ class InviteApiClient(NotifyAdminAPIClient): current_app.config["DANGEROUS_SALT"], ) state_key = f"login-state-{unquote(state)}" - redis_client.set(state_key, state) + redis_client.set(state_key, state, ex=ttl) # make and store the nonce nonce = secrets.token_urlsafe() nonce_key = f"login-nonce-{unquote(nonce)}" - redis_client.set(nonce_key, nonce) # save the nonce to redis. + redis_client.set(nonce_key, nonce, ex=ttl) # save the nonce to redis. data["nonce"] = nonce # This is passed to api for the invite url. data["state"] = state # This is passed to api for the invite url. @@ -64,7 +66,7 @@ class InviteApiClient(NotifyAdminAPIClient): invite_data_key = f"invitedata-{unquote(state)}" redis_invite_data = resp["invite"] redis_invite_data = json.dumps(redis_invite_data) - redis_client.set(invite_data_key, redis_invite_data) + redis_client.set(invite_data_key, redis_invite_data, ex=ttl) return resp_data @@ -97,6 +99,8 @@ class InviteApiClient(NotifyAdminAPIClient): self.post(url=f"/service/{service_id}/invite/{invited_user_id}", data=data) def resend_invite(self, service_id, invited_user_id): + ttl = 24 * 60 * 60 + # make and store the state state = generate_token( str(request.remote_addr), @@ -104,12 +108,12 @@ class InviteApiClient(NotifyAdminAPIClient): current_app.config["DANGEROUS_SALT"], ) state_key = f"login-state-{unquote(state)}" - redis_client.set(state_key, state) + redis_client.set(state_key, state, ex=ttl) # make and store the nonce nonce = secrets.token_urlsafe() nonce_key = f"login-nonce-{unquote(nonce)}" - redis_client.set(nonce_key, nonce) + redis_client.set(nonce_key, nonce, ex=ttl) data = { "nonce": nonce, @@ -122,7 +126,7 @@ class InviteApiClient(NotifyAdminAPIClient): invite_data_key = f"invitedata-{unquote(state)}" redis_invite_data = resp["invite"] redis_invite_data = json.dumps(redis_invite_data) - redis_client.set(invite_data_key, redis_invite_data) + redis_client.set(invite_data_key, redis_invite_data, ex=ttl) @cache.delete("service-{service_id}") @cache.delete("user-{invited_user_id}") diff --git a/app/notify_client/user_api_client.py b/app/notify_client/user_api_client.py index 3514a0d6e..4a41ac28f 100644 --- a/app/notify_client/user_api_client.py +++ b/app/notify_client/user_api_client.py @@ -116,7 +116,7 @@ class UserApiClient(NotifyAdminAPIClient): data["next"] = next_string if code_type == "email": data["email_auth_link_host"] = self.admin_url - endpoint = "/user/{0}/{1}-code".format(user_id, code_type) + endpoint = f"/user/{user_id}/{code_type}-code" current_app.logger.warn(hilite(f"Sending verify_code {code_type} to {user_id}")) self.post(endpoint, data=data)