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:
josephschorr 2017-12-07 14:21:32 -05:00 committed by GitHub
commit b2db266747
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 2 deletions

View file

@ -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'

View file

@ -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)

View file

@ -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