예제 #1
0
def test_login_token_expired(dummy_request, dummy_user):
    email_address = dummy_user.email_address
    _, user_id = save(dummy_user)

    token = '123456'
    token_hash, token_salt = hash_plaintext(token)
    recovery_token = RecoveryToken(
        for_user_id=user_id,
        token_hash=token_hash,
        token_salt=token_salt,
        expires_on=datetime.datetime.utcnow() - datetime.timedelta(hours=1)
    )
    _, recovery_token_id = save(recovery_token)

    data = {
        'email_address': email_address,
        'token': token
    }
    auth_manager = AuthWithRecoveryTokenManager(dummy_request)

    with pytest.raises(HTTPBadRequest) as bad_request:
        auth_manager.login(data)

    unaltered_recovery_token = get_recovery_token_by_id(recovery_token_id)
    assert not unaltered_recovery_token.used
    assert bad_request.value.json == {
        'message': {
            'token': [auth_manager.invalid_credentials_error]
        }
    }
예제 #2
0
def test_login_token_incorrect(dummy_request, dummy_user):
    email_address = dummy_user.email_address
    _, user_id = save(dummy_user)

    token_hash, token_salt = hash_plaintext('123456')
    recovery_token = RecoveryToken(
        for_user_id=user_id,
        token_hash=token_hash,
        token_salt=token_salt
    )
    _, recovery_token_id = save(recovery_token)

    data = {
        'email_address': email_address,
        'token': 'wrong1'
    }
    auth_manager = AuthWithRecoveryTokenManager(dummy_request)

    with pytest.raises(HTTPBadRequest) as bad_request:
        auth_manager.login(data)

    unaltered_recovery_token = get_recovery_token_by_id(recovery_token_id)
    assert not unaltered_recovery_token.used
    assert bad_request.value.json == {
        'message': {
            'token': [auth_manager.invalid_credentials_error]
        }
    }
예제 #3
0
def test_login_user_not_found(dummy_request):
    data = {
        'email_address': '{{cookiecutter.alternative_test_email_address}}',
        'token': '123456'
    }
    auth_manager = AuthWithRecoveryTokenManager(dummy_request)

    with pytest.raises(HTTPBadRequest) as bad_request:
        auth_manager.login(data)

    assert bad_request.value.json == {
        'message': {
            'token': [auth_manager.invalid_credentials_error]
        }
    }
예제 #4
0
def test_locked_after_too_many_attempts(dummy_request):
    data = {'email_address': '{{cookiecutter.test_email_address}}', 'token': 'wrong1'}
    auth_manager = AuthWithRecoveryTokenManager(dummy_request)

    for _ in range(0, 3):
        try:
            auth_manager.login(data)
        except HTTPBadRequest:
            pass

    with pytest.raises(HTTPBadRequest) as bad_request:
        auth_manager.login(data)

    assert "This account is locked" in bad_request.value.json.get('message')
예제 #5
0
def test_login_success(dummy_request, dummy_user):
    email_address = dummy_user.email_address
    _, user_id = save(dummy_user)

    token = '123456'
    token_hash, token_salt = hash_plaintext(token)
    recovery_token = RecoveryToken(
        for_user_id=user_id,
        token_hash=token_hash,
        token_salt=token_salt
    )
    _, recovery_token_id = save(recovery_token)

    data = {
        'email_address': email_address,
        'token': token
    }

    AuthWithRecoveryTokenManager(dummy_request).login(data)

    updated_recovery_token = get_recovery_token_by_id(recovery_token_id)
    assert dummy_request.response.status_code == HTTPStatus.OK
    assert 'auth_tkt' in dummy_request.response.headers['Set-Cookie']
    assert updated_recovery_token.used
예제 #6
0
def test_logout_success(dummy_request, dummy_user):
    user, user_id = save(dummy_user)
    token = '123456'
    token_hash, token_salt = hash_plaintext(token)
    recovery_token = RecoveryToken(
        for_user_id=user_id,
        token_hash=token_hash,
        token_salt=token_salt
    )
    save(recovery_token)

    auth_manager = AuthWithRecoveryTokenManager(dummy_request)
    auth_manager.login({
        'email_address': user.email_address,
        'token': token
    })

    auth_manager.logout()

    assert 'auth_tkt=;' in dummy_request.response.headers['Set-Cookie']
예제 #7
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.auth_manager = AuthWithRecoveryTokenManager(self.request)
예제 #8
0
class AccountRecoveryHandler(LoginHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.auth_manager = AuthWithRecoveryTokenManager(self.request)

    @validate(AccountRecoverySchema())
    @view_config(
        path_hints=['/auth/recover-account'],
        request_schema_class=AccountRecoverySchema,
        permission='recovery.request_token',
        tags=['authentication', 'account recovery'],
        request_method='POST',
        public_hint=True
    )
    def request_account_recovery_token(self, request_data):
        response = HTTPCreated()
        token = token_hex(NUMBER_OF_TOKEN_BYTES)
        email_address = request_data['email_address']
        self._prevent_user_enumeration()

        try:
            recipient = get_one_user_by_email_address(email_address)
            self._invalidate_any_current_recovery_token(recipient)
            self._save_recovery_token(recipient, token)
            SendGridClient().send_account_recovery_email(email_address, token)
        except NoResultFound:
            # To avoid user enumeration we don't indicate failure.
            pass

        raise response

    @staticmethod
    def _prevent_user_enumeration():
        time.sleep(random.randint(
            MIN_TIME_PADDING_IN_DECISECONDS,
            MAX_TIME_PADDING_IN_DECISECONDS
        ) / 10)

    @staticmethod
    def _invalidate_any_current_recovery_token(user):
        try:
            user.active_recovery_token.invalidate()
        except AttributeError:
            pass

    @staticmethod
    def _save_recovery_token(for_user: User, token: str):
        token_hash, token_salt = hash_plaintext(token)
        recovery_token = RecoveryToken(
            token_hash=token_hash,
            token_salt=token_salt,
            for_user=for_user
        )
        save(recovery_token)

    @validate(AccountRecoveryLoginSchema())
    @view_config(
        path_hints=['/auth/recover-account/login'],
        request_schema_class=AccountRecoveryLoginSchema,
        permission='recovery.login',
        request_method='POST',
        successful_response_code=200,
        tags=['authentication', 'account recovery'],
        name='login',
        public_hint=True
    )
    def login(self, login_data):
        self.auth_manager.login(login_data)
        raise self.request.response