"""Test logic related to tokens.""" import time from unittest.mock import MagicMock from unittest.mock import Mock import hashids import pytest from auth.logic import token from auth.models.token import TYPE from auth.utils import date def fixture_token(token_type, expires_in=1200): """Create a token. Args: token_type (str): the token type. expires_in (int): the expiration. Return: Response: the token information. """ return token.create( token_type, 'chef_boyardee', 20982, 28873, expires_in=expires_in) def test_generate_token_unicity(): """Test generating a random token.""" now = int(date.now_utc_timestamp()) current_token = token.generate_token('michael', now) # The token should differ everytime it is being called. assert current_token != token.generate_token('michael', now) def test_token_decrypt(): """Test decrypting a token.""" now = int(date.now_utc_timestamp()) login = 'michael' current_token = token.generate_token(login, now) user_hash = hashids.Hashids(login) time, salt = user_hash.decode(current_token) assert time == now def test_generate_token_missing_data(): """Assert 400 status and error returned if no username given.""" val = token.create( None, 'msamuels', 7123, 123, expires_in=3600, user_ip='192.168.31.41') assert val.status == 501 assert len(val.errors) == 1 assert val.errors.get('type') def test_code_token(monkeypatch): """Test the code token is created and asks for an expiration of an hour.""" monkeypatch.setattr(token, 'create', MagicMock()) token.create_code_token( 'msamuels', 7123, 123, user_ip='192.168.31.41') assert token.create.called assert token.create.call_args[1].get('expires_in') == 600 def test_create_token(monkeypatch): """Test the code token is created and asks for an expiration of an hour.""" monkeypatch.setattr(token, 'create', MagicMock()) token.create_code_token( 'msamuels', 7123, 123, user_ip='192.168.31.41') assert token.create.called assert token.create.call_args[0][0] == TYPE.CODE assert token.create.call_args[1].get('expires_in') == 600 def test_connection_token(monkeypatch): """Test the code token is created and asks for an expiration of an hour.""" monkeypatch.setattr(token, 'create', MagicMock()) token.create_connection_token( 'msamuels', 7123, 123, user_ip='192.168.31.41') assert token.create.called assert token.create.call_args[0][0] == TYPE.CONNECTION assert token.create.call_args[1].get('expires_in') == 2592000 # 30 days def test_lost_password_token_success(monkeypatch): """Test the lost password token is created with a 24 hour expiration.""" token_resp = Mock() token_resp.token = 'rAn12345' monkeypatch.setattr(token, 'create', MagicMock(return_value=token_resp)) val = token.create_password_token( 'mthomas', 7123, 123, user_ip='192.168.31.41') assert token.create.called assert token.create.call_args[0][0] == TYPE.LOST_PASSWORD assert token.create.call_args[1].get('expires_in') == 86400 assert token.create.return_value.token == token_resp.token assert val.token == token.create.return_value.token def test_validate_missing_input(): """Test validating token with missing input.""" assert not token.validate(None, 1093, TYPE.CONNECTION).success assert not token.validate('token', None, TYPE.CONNECTION).success assert not token.validate('', 0, TYPE.CONNECTION).success def test_validate_unknown_token(monkeypatch): """Test validating a token.""" assert not token.validate('token', 1093, TYPE.CONNECTION).success def test_validate_expired_token(monkeypatch): """Test validating expired token.""" current_token = fixture_token(TYPE.CONNECTION, expires_in=1) time.sleep(2) assert not token.validate_connection_token( current_token.token, current_token.client_id).success def test_validate_unauthorized_token(monkeypatch): """Test validating token not owned by a user or a client.""" login = 'michael' client_id = 28873 uid = 20982 current_token = token.create_connection_token(login, uid, client_id) assert not token.validate_connection_token( current_token.token, 'client').success assert not token.validate_connection_token('uuid', client_id).success def test_validate_token(monkeypatch): """Test validating a token.""" login = 'michael' client_id = 28873 uid = 20982 current_token = token.create_connection_token(login, uid, client_id) assert token.validate_connection_token( current_token.token, client_id).success def test_validate_password_token_with_empty_token(): """Test validating password token with an empty input.""" assert not token.validate_password_token('').success assert not token.validate_password_token(None).success def test_validate_unknown_password_token(): """Test validating an unknown password token.""" assert not token.validate_password_token('token').success def test_validate_revoked_password_token(): """Test trying to validate a revoked password token.""" current_token = fixture_token(TYPE.LOST_PASSWORD) token.revoke(current_token.token) assert not token.validate_password_token(current_token.token).success def test_validating_non_matching_password_token(): """Test validating a non matching password token.""" current_token = fixture_token(TYPE.CODE) assert not token.validate_password_token(current_token.token).success def test_validating_expired_password_token(): """Test validating an expired password token.""" current_token = fixture_token(TYPE.LOST_PASSWORD, expires_in=1) time.sleep(3) assert not token.validate_password_token(current_token.token).success def test_validating_valid_password_token(): """Test validating a valid password token.""" current_token = fixture_token(TYPE.LOST_PASSWORD, expires_in=1) assert token.validate_password_token(current_token.token).success def test_revoke_token(): """Test revoking a users toke.""" current_token = fixture_token(TYPE.CONNECTION) assert token.revoke(current_token.token).success def test_revoke_token_twice(): """Test revoking a token twice. The system should return that the token has already been revoked. """ current_token = fixture_token(TYPE.CONNECTION, expires_in=10) token.revoke(current_token.token).success response = token.revoke(current_token.token) assert response.errors.get('token') assert not response.success def test_lost_password_token_failure(monkeypatch): """Test creation error is bubbled up through lost password helper method. Verify that potential DynamoDB exception bubbles to the client. """ failure = Exception('Something bad happened') monkeypatch.setattr(token, 'create', MagicMock(side_effect=failure)) with pytest.raises(Exception): token.create_password_token( 'bad', 7123, 123, user_ip='192.168.31.41') assert token.create.called def test_fetch_success(): """Test fetching a token successfull.""" current_token = fixture_token(TYPE.LOST_PASSWORD, expires_in=10) fetched_token = token.fetch_token(current_token.token) assert fetched_token.token == current_token.token def test_fetch_failure(): """Test fetching a token that doesn't exist fail.""" fetched_token = token.fetch_token('somenonsense') assert not fetched_token.success assert fetched_token.errors.get('token') == token.ERROR_MISSING_TOKEN def test_update_token_expiration(): """Test updating a token's expiratio.""" sleep_time = 2 current_token = fixture_token(TYPE.LOST_PASSWORD, expires_in=10) time.sleep(sleep_time) resp = token.update_token_expiration(current_token.token, 1200) assert resp.success current_token = token.fetch_token(current_token.token) # see the update_token_expiration documentation for more details. assert current_token.expires_in == (1200 + sleep_time) def test_update_token_expiration_with_an_expired_token(): """Test updating a token's expiration on an expired token.""" current_token = fixture_token(TYPE.LOST_PASSWORD, expires_in=2) time.sleep(5) resp = token.update_token_expiration(current_token.token, 1200) assert not resp.success @pytest.mark.parametrize('token_type', [(TYPE.CONNECTION,), (TYPE.CODE,)]) def test_update_token_expiration_with_a_wrong_token_type(token_type): """Test updating a token's expiration on a non PASSWORD_LOST token.""" current_token = fixture_token(token_type) resp = token.update_token_expiration(current_token.token, 1200) assert not resp.success