From 52342afe3f2bc020a7a6f79ec8b0ac2a24710af8 Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Wed, 15 Feb 2017 16:18:05 +0000 Subject: [PATCH] Add a limit to the number of active 2fa codes that we create. At the moment that is set to 10. --- app/config.py | 1 + app/dao/users_dao.py | 8 ++++++++ app/user/rest.py | 7 ++++++- tests/app/dao/test_users_dao.py | 9 +++++++-- tests/app/user/test_rest_verify.py | 22 ++++++++++++++++++++++ 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/app/config.py b/app/config.py index 45762a33a..10c66a9f3 100644 --- a/app/config.py +++ b/app/config.py @@ -78,6 +78,7 @@ class Config(object): SMS_CHAR_COUNT_LIMIT = 495 BRANDING_PATH = '/images/email-template/crests/' TEST_MESSAGE_FILENAME = 'Test message' + MAX_VERIFY_CODE_COUNT = 10 NOTIFY_SERVICE_ID = 'd6aa2c68-a2d9-4437-ab19-3ae8eb202553' INVITATION_EMAIL_TEMPLATE_ID = '4f46df42-f795-4cc4-83bb-65ca312f49cc' diff --git a/app/dao/users_dao.py b/app/dao/users_dao.py index afbd0362c..34360ddc7 100644 --- a/app/dao/users_dao.py +++ b/app/dao/users_dao.py @@ -82,6 +82,14 @@ def delete_user_verify_codes(user): db.session.commit() +def count_user_verify_codes(user): + query = db.session.query( + func.count().label('count') + ).filter(VerifyCode.user == user, + VerifyCode.expiry_datetime <= datetime.utcnow()).one() + return query.count + + def get_user_by_id(user_id=None): if user_id: return User.query.filter_by(id=user_id).one() diff --git a/app/user/rest.py b/app/user/rest.py index 003cc1cd7..9434e80d4 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -12,7 +12,8 @@ from app.dao.users_dao import ( get_user_by_email, create_secret_code, save_user_attribute, - update_user_password + update_user_password, + count_user_verify_codes ) from app.dao.permissions_dao import permission_dao from app.dao.services_dao import dao_fetch_service_by_id @@ -137,6 +138,10 @@ def send_user_sms_code(user_id): user_to_send_to = get_user_by_id(user_id=user_id) verify_code, errors = request_verify_code_schema.load(request.get_json()) + if count_user_verify_codes(user_to_send_to) >= current_app.config.get('MAX_VERIFY_CODE_COUNT'): + # Prevent more than `MAX_VERIFY_CODE_COUNT` active verify codes at a time + return jsonify({}), 204 + secret_code = create_secret_code() create_user_code(user_to_send_to, secret_code, SMS_TYPE) diff --git a/tests/app/dao/test_users_dao.py b/tests/app/dao/test_users_dao.py index e18ef6b79..ed381044e 100644 --- a/tests/app/dao/test_users_dao.py +++ b/tests/app/dao/test_users_dao.py @@ -14,8 +14,8 @@ from app.dao.users_dao import ( reset_failed_login_count, get_user_by_email, delete_codes_older_created_more_than_a_day_ago, - update_user_password -) + update_user_password, + count_user_verify_codes) from app.models import User, VerifyCode @@ -140,3 +140,8 @@ def test_update_user_password(notify_api, notify_db, notify_db_session, sample_u assert not sample_user.check_password(password) update_user_password(sample_user, password) assert sample_user.check_password(password) + + +def test_count_user_verify_codes(sample_user): + [make_verify_code(sample_user) for i in range(5)] + assert count_user_verify_codes(sample_user) == 5 diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index 0f5628d7f..2a76be285 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -249,6 +249,28 @@ def test_send_sms_code_returns_404_for_bad_input_data(client): assert json.loads(resp.get_data(as_text=True))['message'] == 'No result found' +def test_send_sms_code_returns_204_when_too_many_codes_already_created(client, sample_user): + for i in range(10): + verify_code = VerifyCode( + code_type='sms', + _code=12345, + created_at=datetime.utcnow() - timedelta(minutes=10), + expiry_datetime=datetime.utcnow(), + user=sample_user + ) + db.session.add(verify_code) + db.session.commit() + assert VerifyCode.query.count() == 10 + data = json.dumps({}) + auth_header = create_authorization_header() + resp = client.post( + url_for('user.send_user_sms_code', user_id=sample_user.id), + data=data, + headers=[('Content-Type', 'application/json'), auth_header]) + assert resp.status_code == 204 + assert VerifyCode.query.count() == 10 + + def test_send_user_email_verification(client, sample_user, mocker,