Merge pull request #2935 from coreos-inc/joseph.schorr/QS-80/password-reset-expire
Add maximum lifetime of 30m on password recovery tokens
This commit is contained in:
commit
b2db266747
3 changed files with 33 additions and 2 deletions
|
@ -494,3 +494,6 @@ class DefaultConfig(ImmutableConfig):
|
||||||
|
|
||||||
# Defines a secret for enabling the health-check endpoint's debug information.
|
# Defines a secret for enabling the health-check endpoint's debug information.
|
||||||
ENABLE_HEALTH_DEBUG_SECRET = None
|
ENABLE_HEALTH_DEBUG_SECRET = None
|
||||||
|
|
||||||
|
# The lifetime for a user recovery token before it becomes invalid.
|
||||||
|
USER_RECOVERY_TOKEN_LIFETIME = '30m'
|
||||||
|
|
|
@ -1,9 +1,30 @@
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mock import patch
|
from mock import patch
|
||||||
|
|
||||||
from data.model.user import create_user_noverify
|
from data.database import EmailConfirmation
|
||||||
|
from data.model.user import create_user_noverify, validate_reset_code
|
||||||
|
from util.timedeltastring import convert_to_timedelta
|
||||||
from test.fixtures import *
|
from test.fixtures import *
|
||||||
|
|
||||||
def test_create_user_with_expiration(initialized_db):
|
def test_create_user_with_expiration(initialized_db):
|
||||||
with patch('data.model.config.app_config', {'DEFAULT_TAG_EXPIRATION': '1h'}):
|
with patch('data.model.config.app_config', {'DEFAULT_TAG_EXPIRATION': '1h'}):
|
||||||
user = create_user_noverify('foobar', 'foo@example.com', email_required=False)
|
user = create_user_noverify('foobar', 'foo@example.com', email_required=False)
|
||||||
assert user.removed_tag_expiration_s == 60 * 60
|
assert user.removed_tag_expiration_s == 60 * 60
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('token_lifetime, time_since', [
|
||||||
|
('1m', '2m'),
|
||||||
|
('2m', '1m'),
|
||||||
|
('1h', '1m'),
|
||||||
|
])
|
||||||
|
def test_validation_code(token_lifetime, time_since, initialized_db):
|
||||||
|
user = create_user_noverify('foobar', 'foo@example.com', email_required=False)
|
||||||
|
created = datetime.now() - convert_to_timedelta(time_since)
|
||||||
|
confirmation = EmailConfirmation.create(user=user, pw_reset=True, created=created)
|
||||||
|
|
||||||
|
with patch('data.model.config.app_config', {'USER_RECOVERY_TOKEN_LIFETIME': token_lifetime}):
|
||||||
|
result = validate_reset_code(confirmation.code)
|
||||||
|
expect_success = convert_to_timedelta(token_lifetime) >= convert_to_timedelta(time_since)
|
||||||
|
assert expect_success == (result is not None)
|
||||||
|
|
|
@ -507,19 +507,26 @@ def create_reset_password_email_code(email):
|
||||||
|
|
||||||
|
|
||||||
def validate_reset_code(code):
|
def validate_reset_code(code):
|
||||||
|
# Find the reset code.
|
||||||
try:
|
try:
|
||||||
code = EmailConfirmation.get(EmailConfirmation.code == code,
|
code = EmailConfirmation.get(EmailConfirmation.code == code,
|
||||||
EmailConfirmation.pw_reset == True)
|
EmailConfirmation.pw_reset == True)
|
||||||
except EmailConfirmation.DoesNotExist:
|
except EmailConfirmation.DoesNotExist:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Make sure the code is not expired.
|
||||||
|
max_lifetime_duration = convert_to_timedelta(config.app_config['USER_RECOVERY_TOKEN_LIFETIME'])
|
||||||
|
if code.created + max_lifetime_duration < datetime.now():
|
||||||
|
code.delete_instance()
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Verify the user and return the code.
|
||||||
user = code.user
|
user = code.user
|
||||||
if not user.verified:
|
if not user.verified:
|
||||||
user.verified = True
|
user.verified = True
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
code.delete_instance()
|
code.delete_instance()
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in a new issue