From 71240bb13dff99564e267d5c50d03840ee62461e Mon Sep 17 00:00:00 2001 From: Kevin Chung Date: Sun, 4 Aug 2019 20:28:20 -0400 Subject: [PATCH] Require password for email change (#1077) * Require password for email changes --- CTFd/schemas/users.py | 15 +++++++++++++++ tests/api/v1/test_users.py | 14 +++++++------- tests/users/test_profile.py | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/CTFd/schemas/users.py b/CTFd/schemas/users.py index 77ad24b1..73ed057d 100644 --- a/CTFd/schemas/users.py +++ b/CTFd/schemas/users.py @@ -119,6 +119,21 @@ class UserSchema(ma.ModelSchema): if email == current_user.email: return data else: + confirm = data.get("confirm") + + if bool(confirm) is False: + raise ValidationError( + "Please confirm your current password", field_names=["confirm"] + ) + + test = verify_password( + plaintext=confirm, ciphertext=current_user.password + ) + if test is False: + raise ValidationError( + "Your previous password is incorrect", field_names=["confirm"] + ) + if existing_user: raise ValidationError( "Email address has already been used", field_names=["email"] diff --git a/tests/api/v1/test_users.py b/tests/api/v1/test_users.py index bea9506b..89e9b97f 100644 --- a/tests/api/v1/test_users.py +++ b/tests/api/v1/test_users.py @@ -204,21 +204,21 @@ def test_api_users_patch_duplicate_information(): # Duplicate email r = client.patch( "/api/v1/users/me", - json={"name": "user2", "email": "user@ctfd.io", "password": "password"}, + json={"name": "user1", "email": "user2@ctfd.io", "confirm": "password"}, ) resp = r.get_json() assert r.status_code == 400 - assert resp["errors"]["name"] + assert resp["errors"]["email"] assert resp["success"] is False # Duplicate user r = client.patch( "/api/v1/users/me", - json={"name": "user", "email": "user2@ctfd.io", "password": "password"}, + json={"name": "user2", "email": "user1@ctfd.io", "confirm": "password"}, ) resp = r.get_json() assert r.status_code == 400 - assert resp["errors"]["email"] + assert resp["errors"]["name"] assert resp["success"] is False assert Users.query.count() == 3 destroy_ctfd(app) @@ -454,7 +454,7 @@ def test_api_user_change_verify_email(): user.verified = True app.db.session.commit() with login_as_user(app) as client: - r = client.patch("/api/v1/users/me", json={"email": "new_email@email.com"}) + r = client.patch("/api/v1/users/me", json={"email": "new_email@email.com", "confirm": "password"}) assert r.status_code == 200 resp = r.get_json() assert resp["data"]["email"] == "new_email@email.com" @@ -473,14 +473,14 @@ def test_api_user_change_email_under_whitelist(): "domain_whitelist", "whitelisted.com, whitelisted.org, whitelisted.net" ) with login_as_user(app) as client: - r = client.patch("/api/v1/users/me", json={"email": "new_email@email.com"}) + r = client.patch("/api/v1/users/me", json={"email": "new_email@email.com", "confirm": "password"}) assert r.status_code == 400 resp = r.get_json() assert resp["errors"]["email"] assert resp["success"] is False r = client.patch( - "/api/v1/users/me", json={"email": "new_email@whitelisted.com"} + "/api/v1/users/me", json={"email": "new_email@whitelisted.com", "confirm": "password"} ) assert r.status_code == 200 resp = r.get_json() diff --git a/tests/users/test_profile.py b/tests/users/test_profile.py index e69de29b..c9db4d5c 100644 --- a/tests/users/test_profile.py +++ b/tests/users/test_profile.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from CTFd.models import Users +from CTFd.utils.crypto import verify_password +from tests.helpers import create_ctfd, destroy_ctfd, register_user, login_as_user + + +def test_email_cannot_be_changed_without_password(): + """Test that a user can't update their email address without current password""" + app = create_ctfd() + with app.app_context(): + register_user(app) + client = login_as_user(app) + + data = {"name": "user", "email": "user2@ctfd.io"} + + r = client.patch("/api/v1/users/me", json=data) + assert r.status_code == 400 + user = Users.query.filter_by(id=2).first() + assert user.email == "user@ctfd.io" + + data = {"name": "user", "email": "user2@ctfd.io", "confirm": "asdf"} + + r = client.patch("/api/v1/users/me", json=data) + assert r.status_code == 400 + user = Users.query.filter_by(id=2).first() + assert user.email == "user@ctfd.io" + + data = {"name": "user", "email": "user2@ctfd.io", "confirm": "password"} + + r = client.patch("/api/v1/users/me", json=data) + assert r.status_code == 200 + user = Users.query.filter_by(id=2).first() + assert user.email == "user2@ctfd.io" + assert verify_password(plaintext="password", ciphertext=user.password) + destroy_ctfd(app)