diff --git a/app/main/dao/users_dao.py b/app/main/dao/users_dao.py index 59a23568c..d4d3a0b42 100644 --- a/app/main/dao/users_dao.py +++ b/app/main/dao/users_dao.py @@ -24,3 +24,9 @@ def get_all_users(): def get_user_by_email(email_address): return User.query.filter_by(email_address=email_address).first() + + +def increment_failed_login_count(id): + user = User.query.filter_by(id=id).first() + user.failed_login_count += 1 + db.session.commit() diff --git a/app/main/views/sign_in.py b/app/main/views/sign_in.py index d5d3677c2..492b3225c 100644 --- a/app/main/views/sign_in.py +++ b/app/main/views/sign_in.py @@ -20,11 +20,14 @@ def process_sign_in(): form = LoginForm() if form.validate_on_submit(): user = users_dao.get_user_by_email(form.email_address.data) + if user.is_locked(): + return jsonify(locked_out=True), 401 if user is None: return jsonify(authorization=False), 401 if checkpw(form.password.data, user.password): login_user(user) else: + users_dao.increment_failed_login_count(user.id) return jsonify(authorization=False), 401 else: return jsonify(form.errors), 400 diff --git a/app/models.py b/app/models.py index 7767c91a8..c7a388423 100644 --- a/app/models.py +++ b/app/models.py @@ -56,7 +56,7 @@ class User(db.Model): return self.id def is_locked(self): - if self.failed_login_count <= current_app.config['MAX_FAILED_LOGIN_COUNT']: + if self.failed_login_count < current_app.config['MAX_FAILED_LOGIN_COUNT']: return False else: return True diff --git a/tests/app/main/dao/test_users_dao.py b/tests/app/main/dao/test_users_dao.py index 694a0fe3b..55b64e508 100644 --- a/tests/app/main/dao/test_users_dao.py +++ b/tests/app/main/dao/test_users_dao.py @@ -71,3 +71,37 @@ def test_get_all_users_returns_all_users(notifications_admin, notifications_admi users = users_dao.get_all_users() assert len(users) == 3 assert users == [user1, user2, user3] + + +def test_increment_failed_lockout_count_should_increade_count_by_1(notifications_admin, notifications_admin_db): + user = User(name='cannot remember password', + password='somepassword', + email_address='test1@get_all.gov.uk', + mobile_number='+441234123412', + created_at=datetime.now(), + role_id=1) + users_dao.insert_user(user) + + savedUser = users_dao.get_user_by_id(user.id) + assert savedUser.failed_login_count == 0 + users_dao.increment_failed_login_count(user.id) + assert users_dao.get_user_by_id(user.id).failed_login_count == 1 + + +def test_user_is_locked_if_failed_login_count_is_10_or_greater(notifications_admin, notifications_admin_db): + user = User(name='cannot remember password', + password='somepassword', + email_address='test1@get_all.gov.uk', + mobile_number='+441234123412', + created_at=datetime.now(), + role_id=1) + users_dao.insert_user(user) + saved_user = users_dao.get_user_by_id(user.id) + assert saved_user.is_locked() is False + + for _ in range(10): + users_dao.increment_failed_login_count(user.id) + + saved_user = users_dao.get_user_by_id(user.id) + assert saved_user.failed_login_count == 10 + assert saved_user.is_locked() is True diff --git a/tests/app/main/views/test_sign_in.py b/tests/app/main/views/test_sign_in.py index 255c9d90c..1c84650c3 100644 --- a/tests/app/main/views/test_sign_in.py +++ b/tests/app/main/views/test_sign_in.py @@ -34,3 +34,24 @@ def test_temp_create_user(notifications_admin, notifications_admin_db): 'password': 'val1dPassw0rd!'}) assert response.status_code == 302 + + +def test_should_return_locked_out_true_when_user_is_locked(notifications_admin, notifications_admin_db): + user = User(email_address='valid@example.gov.uk', + password='val1dPassw0rd!', + mobile_number='+441234123123', + name='valid', + created_at=datetime.now(), + role_id=1) + users_dao.insert_user(user) + for _ in range(10): + notifications_admin.test_client().post('/sign-in', + data={'email_address': 'valid@example.gov.uk', + 'password': 'whatIsMyPassword!'}) + + response = notifications_admin.test_client().post('/sign-in', + data={'email_address': 'valid@example.gov.uk', + 'password': 'val1dPassw0rd!'}) + + assert response.status_code == 401 + assert '"locked_out": true' in response.get_data(as_text=True)