Skip to content

Commit

Permalink
Task/add specific failed comment (#1994)
Browse files Browse the repository at this point in the history
* Add line to log failed login attempt

* fix

* fix

* fixes

* fix
  • Loading branch information
jzbahrai authored Nov 6, 2023
1 parent 11da560 commit 917bf40
Show file tree
Hide file tree
Showing 5 changed files with 1,047 additions and 1,614 deletions.
2 changes: 2 additions & 0 deletions app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ class Config(object):
ONE_OFF_MESSAGE_FILENAME = "Report"
MAX_VERIFY_CODE_COUNT = 10
JOBS_MAX_SCHEDULE_HOURS_AHEAD = 96
FAILED_LOGIN_LIMIT = os.getenv("FAILED_LOGIN_LIMIT", 10)

# be careful increasing this size without being sure that we won't see slowness in pysftp
MAX_LETTER_PDF_ZIP_FILESIZE = 40 * 1024 * 1024 # 40mb
Expand Down Expand Up @@ -689,6 +690,7 @@ class Test(Development):
FF_EMAIL_DAILY_LIMIT = False
CRM_GITHUB_PERSONAL_ACCESS_TOKEN = "test-token"
CRM_ORG_LIST_URL = "https://test-url.com"
FAILED_LOGIN_LIMIT = 0


class Production(Config):
Expand Down
7 changes: 6 additions & 1 deletion app/user/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,12 @@ def verify_user_password(user_id):
return jsonify({}), 204
else:
increment_failed_login_count(user_to_verify)
message = "Incorrect password"
if user_to_verify.failed_login_count >= current_app.config["FAILED_LOGIN_LIMIT"]:
message = "Failed login: Incorrect password for user_id {user_id} failed_login {failed_login_count} times".format(
user_id=user_id, failed_login_count=user_to_verify.failed_login_count
)
else:
message = "Incorrect password for user_id {user_id}".format(user_id=user_id)
errors = {"password": [message]}
raise InvalidRequest(errors, status_code=400)

Expand Down
31 changes: 31 additions & 0 deletions tests/app/user/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1544,3 +1544,34 @@ def test_update_user_blocked(admin_request, sample_user, account_change_template

assert resp["data"]["id"] == str(sample_user.id)
assert resp["data"]["blocked"]


class TestFailedLogin:
def test_update_user_password_saves_correctly(self, client, sample_service, mocker):
sample_user = sample_service.users[0]
new_password = "tQETOgIO8yzDMyCsDjLZIEVZHAvkFArYfmSI1KTsJnlnPohI2tfIa8kfng7bxCm"
data = {"_password": new_password}
auth_header = create_authorization_header()
headers = [("Content-Type", "application/json"), auth_header]
resp = client.post(
url_for("user.update_password", user_id=sample_user.id),
data=json.dumps(data),
headers=headers,
)
assert resp.status_code == 200

json_resp = json.loads(resp.get_data(as_text=True))
assert json_resp["data"]["password_changed_at"] is not None
data = {"password": new_password}
auth_header = create_authorization_header()
headers = [("Content-Type", "application/json"), auth_header]
# We force a the password to fail on login
mocker.patch("app.models.User.check_password", return_value=False)

resp = client.post(
url_for("user.verify_user_password", user_id=str(sample_user.id)),
data=json.dumps(data),
headers=headers,
)
assert resp.status_code == 400
assert "Incorrect password for user_id" in resp.json["message"]["password"][0]
63 changes: 33 additions & 30 deletions tests/app/user/test_rest_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from app.dao.users_dao import create_user_code
from app.models import EMAIL_TYPE, SMS_TYPE, Notification, User, VerifyCode
from tests import create_authorization_header
from tests.conftest import set_config_values


@freeze_time("2016-01-01T12:00:00")
Expand Down Expand Up @@ -111,21 +112,22 @@ def test_user_verify_password_creates_login_event(client, sample_user):
assert len(events) == 1


def test_user_verify_password_invalid_password(client, sample_user):
def test_user_verify_password_invalid_password(client, sample_user, mocker):
data = json.dumps({"password": "bad password"})
auth_header = create_authorization_header()

assert sample_user.failed_login_count == 0

resp = client.post(
url_for("user.verify_user_password", user_id=sample_user.id),
data=data,
headers=[("Content-Type", "application/json"), auth_header],
)
assert resp.status_code == 400
json_resp = json.loads(resp.get_data(as_text=True))
assert "Incorrect password" in json_resp["message"]["password"]
assert sample_user.failed_login_count == 1
with set_config_values(current_app, {"FAILED_LOGIN_LIMIT": 10}):
resp = client.post(
url_for("user.verify_user_password", user_id=sample_user.id),
data=data,
headers=[("Content-Type", "application/json"), auth_header],
)
assert resp.status_code == 400
json_resp = json.loads(resp.get_data(as_text=True))
assert "Incorrect password" in json_resp["message"]["password"][0]
assert sample_user.failed_login_count == 1


def test_user_verify_password_valid_password_resets_failed_logins(client, sample_user):
Expand All @@ -134,27 +136,28 @@ def test_user_verify_password_valid_password_resets_failed_logins(client, sample

assert sample_user.failed_login_count == 0

resp = client.post(
url_for("user.verify_user_password", user_id=sample_user.id),
data=data,
headers=[("Content-Type", "application/json"), auth_header],
)
assert resp.status_code == 400
json_resp = json.loads(resp.get_data(as_text=True))
assert "Incorrect password" in json_resp["message"]["password"]

assert sample_user.failed_login_count == 1

data = json.dumps({"password": "password"})
auth_header = create_authorization_header()
resp = client.post(
url_for("user.verify_user_password", user_id=sample_user.id),
data=data,
headers=[("Content-Type", "application/json"), auth_header],
)
with set_config_values(current_app, {"FAILED_LOGIN_LIMIT": 10}):
resp = client.post(
url_for("user.verify_user_password", user_id=sample_user.id),
data=data,
headers=[("Content-Type", "application/json"), auth_header],
)
assert resp.status_code == 400
json_resp = json.loads(resp.get_data(as_text=True))
assert "Incorrect password" in json_resp["message"]["password"][0]

assert sample_user.failed_login_count == 1

data = json.dumps({"password": "password"})
auth_header = create_authorization_header()
resp = client.post(
url_for("user.verify_user_password", user_id=sample_user.id),
data=data,
headers=[("Content-Type", "application/json"), auth_header],
)

assert resp.status_code == 204
assert sample_user.failed_login_count == 0
assert resp.status_code == 204
assert sample_user.failed_login_count == 0


def test_user_verify_password_missing_password(client, sample_user):
Expand Down
Loading

0 comments on commit 917bf40

Please sign in to comment.