mirror of
https://github.com/GSA/notifications-api.git
synced 2025-12-18 04:01:47 -05:00
Set the expiry time on a verify code (2fa) to 10 minutes.
When the verify code is wrong or expired increment the failed to login count for the user. When the verify code is successfully used reset the failed login count to 0.
This commit is contained in:
@@ -35,7 +35,7 @@ def save_model_user(usr, update_dict={}, pwd=None):
|
||||
|
||||
def create_user_code(user, code, code_type):
|
||||
verify_code = VerifyCode(code_type=code_type,
|
||||
expiry_datetime=datetime.utcnow() + timedelta(hours=1),
|
||||
expiry_datetime=datetime.utcnow() + timedelta(minutes=30),
|
||||
user=user)
|
||||
verify_code.code = code
|
||||
db.session.add(verify_code)
|
||||
|
||||
@@ -123,10 +123,13 @@ def verify_user_code(user_id):
|
||||
|
||||
code = get_user_code(user_to_verify, txt_code, txt_type)
|
||||
if not code:
|
||||
increment_failed_login_count(user_to_verify)
|
||||
raise InvalidRequest("Code not found", status_code=404)
|
||||
if datetime.utcnow() > code.expiry_datetime or code.code_used:
|
||||
increment_failed_login_count(user_to_verify)
|
||||
raise InvalidRequest("Code has expired", status_code=400)
|
||||
use_user_code(code.id)
|
||||
reset_failed_login_count(user_to_verify)
|
||||
return jsonify({}), 204
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import json
|
||||
import moto
|
||||
import pytest
|
||||
|
||||
from datetime import (
|
||||
@@ -15,7 +14,7 @@ from app.models import (
|
||||
Notification
|
||||
)
|
||||
|
||||
from app import db, encryption
|
||||
from app import db
|
||||
|
||||
from tests import create_authorization_header
|
||||
from freezegun import freeze_time
|
||||
@@ -23,198 +22,167 @@ from freezegun import freeze_time
|
||||
import app.celery.tasks
|
||||
|
||||
|
||||
def test_user_verify_code_sms(notify_api,
|
||||
sample_sms_code):
|
||||
"""
|
||||
Tests POST endpoint '/<user_id>/verify/code'
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert not VerifyCode.query.first().code_used
|
||||
data = json.dumps({
|
||||
'code_type': sample_sms_code.code_type,
|
||||
'code': sample_sms_code.txt_code})
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_sms_code.user.id),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 204
|
||||
assert VerifyCode.query.first().code_used
|
||||
def test_user_verify_code(client,
|
||||
sample_sms_code):
|
||||
assert not VerifyCode.query.first().code_used
|
||||
data = json.dumps({
|
||||
'code_type': sample_sms_code.code_type,
|
||||
'code': sample_sms_code.txt_code})
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_sms_code.user.id),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 204
|
||||
assert VerifyCode.query.first().code_used
|
||||
|
||||
|
||||
def test_user_verify_code_sms_missing_code(notify_api,
|
||||
sample_sms_code):
|
||||
"""
|
||||
Tests POST endpoint '/<user_id>/verify/code'
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert not VerifyCode.query.first().code_used
|
||||
data = json.dumps({'code_type': sample_sms_code.code_type})
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_sms_code.user.id),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 400
|
||||
assert not VerifyCode.query.first().code_used
|
||||
def test_user_verify_code_missing_code(client,
|
||||
sample_sms_code):
|
||||
assert not VerifyCode.query.first().code_used
|
||||
data = json.dumps({'code_type': sample_sms_code.code_type})
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_sms_code.user.id),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 400
|
||||
assert not VerifyCode.query.first().code_used
|
||||
assert User.query.get(sample_sms_code.user.id).failed_login_count == 0
|
||||
|
||||
|
||||
@moto.mock_sqs
|
||||
def test_user_verify_code_email(notify_api,
|
||||
sqs_client_conn,
|
||||
sample_email_code):
|
||||
"""
|
||||
Tests POST endpoint '/<user_id>/verify/code'
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert not VerifyCode.query.first().code_used
|
||||
data = json.dumps({
|
||||
'code_type': sample_email_code.code_type,
|
||||
'code': sample_email_code.txt_code})
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_email_code.user.id),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 204
|
||||
assert VerifyCode.query.first().code_used
|
||||
def test_user_verify_code_bad_code_and_increments_failed_login_count(client,
|
||||
sample_sms_code):
|
||||
assert not VerifyCode.query.first().code_used
|
||||
data = json.dumps({
|
||||
'code_type': sample_sms_code.code_type,
|
||||
'code': "blah"})
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_sms_code.user.id),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 404
|
||||
assert not VerifyCode.query.first().code_used
|
||||
assert User.query.get(sample_sms_code.user.id).failed_login_count == 1
|
||||
|
||||
|
||||
def test_user_verify_code_email_bad_code(notify_api,
|
||||
sample_email_code):
|
||||
"""
|
||||
Tests POST endpoint '/<user_id>/verify/code'
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert not VerifyCode.query.first().code_used
|
||||
data = json.dumps({
|
||||
'code_type': sample_email_code.code_type,
|
||||
'code': "blah"})
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_email_code.user.id),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 404
|
||||
assert not VerifyCode.query.first().code_used
|
||||
def test_user_verify_code_resets_failed_login_count_when_successful(client, sample_sms_code):
|
||||
assert not VerifyCode.query.first().code_used
|
||||
data = json.dumps({
|
||||
'code_type': sample_sms_code.code_type,
|
||||
'code': "blah"})
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_sms_code.user.id),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 404
|
||||
assert not VerifyCode.query.first().code_used
|
||||
assert User.query.get(sample_sms_code.user.id).failed_login_count == 1
|
||||
correct_data = json.dumps({
|
||||
'code_type': sample_sms_code.code_type,
|
||||
'code': sample_sms_code.txt_code})
|
||||
success_response = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_sms_code.user.id),
|
||||
data=correct_data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert success_response.status_code == 204
|
||||
assert VerifyCode.query.first().code_used
|
||||
assert User.query.get(sample_sms_code.user.id).failed_login_count == 0
|
||||
|
||||
|
||||
def test_user_verify_code_email_expired_code(notify_api,
|
||||
sample_email_code):
|
||||
"""
|
||||
Tests POST endpoint '/<user_id>/verify/code'
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert not VerifyCode.query.first().code_used
|
||||
sample_email_code.expiry_datetime = (
|
||||
datetime.utcnow() - timedelta(hours=1))
|
||||
db.session.add(sample_email_code)
|
||||
db.session.commit()
|
||||
data = json.dumps({
|
||||
'code_type': sample_email_code.code_type,
|
||||
'code': sample_email_code.txt_code})
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_email_code.user.id),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 400
|
||||
assert not VerifyCode.query.first().code_used
|
||||
def test_user_verify_code_expired_code_and_increments_failed_login_count(
|
||||
client,
|
||||
sample_sms_code):
|
||||
assert not VerifyCode.query.first().code_used
|
||||
sample_sms_code.expiry_datetime = (
|
||||
datetime.utcnow() - timedelta(hours=1))
|
||||
db.session.add(sample_sms_code)
|
||||
db.session.commit()
|
||||
data = json.dumps({
|
||||
'code_type': sample_sms_code.code_type,
|
||||
'code': sample_sms_code.txt_code})
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.verify_user_code', user_id=sample_sms_code.user.id),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 400
|
||||
assert not VerifyCode.query.first().code_used
|
||||
assert User.query.get(sample_sms_code.user.id).failed_login_count == 1
|
||||
|
||||
|
||||
@freeze_time("2016-01-01 10:00:00.000000")
|
||||
def test_user_verify_password(notify_api,
|
||||
notify_db,
|
||||
def test_user_verify_password(client,
|
||||
notify_db_session,
|
||||
sample_user):
|
||||
"""
|
||||
Tests POST endpoint '/<user_id>/verify/password'
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
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
|
||||
User.query.get(sample_user.id).logged_in_at == datetime.utcnow()
|
||||
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 User.query.get(sample_user.id).logged_in_at == datetime.utcnow()
|
||||
|
||||
|
||||
def test_user_verify_password_invalid_password(notify_api,
|
||||
def test_user_verify_password_invalid_password(client,
|
||||
sample_user):
|
||||
"""
|
||||
Tests POST endpoint '/<user_id>/verify/password' invalid endpoint.
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
data = json.dumps({'password': 'bad password'})
|
||||
auth_header = create_authorization_header()
|
||||
data = json.dumps({'password': 'bad password'})
|
||||
auth_header = create_authorization_header()
|
||||
|
||||
assert sample_user.failed_login_count == 0
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
def test_user_verify_password_valid_password_resets_failed_logins(notify_api,
|
||||
def test_user_verify_password_valid_password_resets_failed_logins(client,
|
||||
sample_user):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
data = json.dumps({'password': 'bad password'})
|
||||
auth_header = create_authorization_header()
|
||||
data = json.dumps({'password': 'bad password'})
|
||||
auth_header = create_authorization_header()
|
||||
|
||||
assert sample_user.failed_login_count == 0
|
||||
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']
|
||||
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
|
||||
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])
|
||||
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(notify_api,
|
||||
def test_user_verify_password_missing_password(client,
|
||||
sample_user):
|
||||
"""
|
||||
Tests POST endpoint '/<user_id>/verify/password' missing password.
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
data = json.dumps({'bingo': 'bongo'})
|
||||
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 == 400
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert 'Required field missing data' in json_resp['message']['password']
|
||||
data = json.dumps({'bingo': 'bongo'})
|
||||
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 == 400
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert 'Required field missing data' in json_resp['message']['password']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('research_mode', [True, False])
|
||||
@@ -293,22 +261,17 @@ def test_send_user_code_for_sms_with_optional_to_field(notify_api,
|
||||
)
|
||||
|
||||
|
||||
def test_send_sms_code_returns_404_for_bad_input_data(notify_api, notify_db, notify_db_session):
|
||||
"""
|
||||
Tests POST endpoint /user/<user_id>/sms-code return 404 for bad input data
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
data = json.dumps({})
|
||||
import uuid
|
||||
uuid_ = uuid.uuid4()
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.send_user_sms_code', user_id=uuid_),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 404
|
||||
assert json.loads(resp.get_data(as_text=True))['message'] == 'No result found'
|
||||
def test_send_sms_code_returns_404_for_bad_input_data(client):
|
||||
data = json.dumps({})
|
||||
import uuid
|
||||
uuid_ = uuid.uuid4()
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.post(
|
||||
url_for('user.send_user_sms_code', user_id=uuid_),
|
||||
data=data,
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert resp.status_code == 404
|
||||
assert json.loads(resp.get_data(as_text=True))['message'] == 'No result found'
|
||||
|
||||
|
||||
def test_send_user_email_verification(client,
|
||||
|
||||
Reference in New Issue
Block a user