Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added integration test for password reset link at-most-once behavior #282

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions integration_tests/assets/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,7 @@ services:

smtp:
image: munkyboy/fakesmtp
ulimits:
nofile: 65536
ports:
- "25"
15 changes: 15 additions & 0 deletions integration_tests/suite/helpers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,25 @@ def setUpClass(cls):
cls.reset_clients()
cls.top_tenant_uuid = cls.get_top_tenant()['uuid']

@classmethod
def tearDownClass(cls):
super().tearDownClass()
# cls.reset_clients()

def setUp(self):
super().setUp()
print("Starting test case")
token = self.client.token.new(expiration=7200)
print("token=", token)
self.client.set_token(token['token'])
self.admin_token = token['token']
self.admin_user_uuid = token['metadata']['uuid']

@classmethod
def reset_clients(cls):
cls.client = cls.asset_cls.make_auth_client(cls.username, cls.password)
token = cls.client.token.new(expiration=7200)
print("token=", token)
cls.client.set_token(token['token'])
cls.admin_token = token['token']
cls.admin_user_uuid = token['metadata']['uuid']
Expand Down
7 changes: 6 additions & 1 deletion integration_tests/suite/helpers/fixtures/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

from contextlib import contextmanager
from functools import wraps
import logging

logger = logging.getLogger(__name__)


def _random_string(length):
Expand Down Expand Up @@ -76,7 +79,9 @@ def wrapper(self, *args, **kwargs):
try:
self.client.users.delete(user['uuid'])
except requests.HTTPError:
pass
logger.exception("Failed to delete user")
# pass
raise
return result

return wrapper
Expand Down
35 changes: 34 additions & 1 deletion integration_tests/suite/test_password_reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@

from .helpers import fixtures, base
from .helpers.base import assert_http_error, assert_no_error
import pytest
from requests import HTTPError


@base.use_asset('base')
class TestResetPassword(base.APIIntegrationTest):
def setUp(self):
super().setUp()
users = self.client.users.list()
print("users: ", users)

@fixtures.http.user(username='foo', email_address='[email protected]')
@fixtures.http.user(username='bar', email_address='[email protected]')
@fixtures.http.user(username=None, email_address='[email protected]')
Expand Down Expand Up @@ -50,13 +57,39 @@ def test_password_reset(self, foo, bar, u3, u4):
assert_no_error(user_client.token.new, 'wazo_user', expiration=1)

def _update_password_from_email(self, raw_email, password):
token, user_uuid = self._get_reset_token_from_email(raw_email)
return self.client.users.set_password(user_uuid, password, token)

def _get_reset_token_from_email(self, raw_email):
headers, body = raw_email.split('\n\n', 1)
email_fields = yaml.safe_load(body)

token = email_fields['token']
user_uuid = email_fields['user_uuid']

return self.client.users.set_password(user_uuid, password, token)
return token, user_uuid

@fixtures.http.user(username='[email protected]', email_address='[email protected]')
def test_password_reset_unipotence(self, foo):
self.clean_emails()
self.client.users.reset_password(username=foo['username'])
emails = self.get_emails()

assert_that(
emails,
contains_inanyorder(
contains_string('username: [email protected]'),
),
)
new_password = '5ecr37'
email = emails[0]
token, user_uuid = self._get_reset_token_from_email(email)
self.client.users.set_password(user_uuid, new_password, token)
# second time token has been revoked
new_new_password = 'abcdef5'
with pytest.raises(HTTPError) as exc_info:
self.client.users.set_password(user_uuid, new_new_password, token)
assert exc_info.value.response.status_code == 401

@fixtures.http.user(username='foobar')
def test_set_password(self, user):
Expand Down
6 changes: 6 additions & 0 deletions wazo_auth/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ def is_valid(self, token_id, required_access):
except exceptions.MissingAccessTokenException:
return False

def revoke(self, token_id):
try:
current_app.config['token_service'].remove_token(token_id)
except exceptions.UnknownTokenException:
raise Unauthorized(token_id)

def get(self, token_id, required_access=None):
try:
return (
Expand Down
3 changes: 3 additions & 0 deletions wazo_auth/plugins/http/password_reset/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ def post(self):
user_uuid, None, args['password'], reset=True
)

# reset token shouldn't be reused
self.auth_client.token.revoke(token_id)

return '', 204

def _extract_email(self, user):
Expand Down