vcrpy icon indicating copy to clipboard operation
vcrpy copied to clipboard

Doesn't play nicely with requests-ntlm

Open ghost opened this issue 6 years ago • 3 comments

Here's what happens when you use auth=requests_ntlm.HttpNtlmAuth(username, password):

  File "/venv/lib/python3.5/site-packages/requests/api.py", line 72, in get
    return request('get', url, params=params, **kwargs)
  File "/venv/lib/python3.5/site-packages/requests/api.py", line 58, in request
    return session.request(method=method, url=url, **kwargs)
  File "/venv/lib/python3.5/site-packages/requests/sessions.py", line 512, in request
    resp = self.send(prep, **send_kwargs)
  File "/venv/lib/python3.5/site-packages/requests/sessions.py", line 629, in send
    r = dispatch_hook('response', hooks, r, **kwargs)
  File "/venv/lib/python3.5/site-packages/requests/hooks.py", line 31, in dispatch_hook
    _hook_data = hook(hook_data, **kwargs)
  File "/venv/lib/python3.5/site-packages/requests_ntlm/requests_ntlm.py", line 151, in response_hook
    kwargs
  File "/venv/lib/python3.5/site-packages/requests_ntlm/requests_ntlm.py", line 52, in retry_using_http_NTLM_auth
    server_certificate_hash = self._get_server_cert(response)
  File "/venv/lib/python3.5/site-packages/requests_ntlm/requests_ntlm.py", line 187, in _get_server_cert
    socket = raw_response._fp.fp.raw._sock
AttributeError: 'VCRHTTPResponse' object has no attribute 'fp'

ghost avatar Jan 19 '19 11:01 ghost

A lot of changes have happened to VCRpy since this ticket was opened. As this ticket has become stale, would you mind closing it if it is no longer needed / relevant?

If I haven't heard anything in a week I'll mark it as closed as we have a lot of old tickets that need to be groomed to make it easier to see what is still relevant.

However if it is still needed, please feel free to re-open or create a new ticket.

Thanks! 🙏

neozenith avatar Jan 05 '20 23:01 neozenith

I'm having a similar issue with NTLM authentication. I've managed to come up with a small pytest script that uses a public NTLM test webpage that shows the issue.

import requests
import requests_ntlm
import vcr


def test_ntlm_auth():
    resp = requests.get(
        "http://ntlm.herokuapp.com/",
        auth=requests_ntlm.HttpNtlmAuth("user", "pass"),
    )
    assert "ello" in str(resp.content)


def test_ntlm_auth_with_vcr():
    with vcr.use_cassette("/tmp/test_ntlm_auth.yaml"):
        resp = requests.get(
            "http://ntlm.herokuapp.com/",
            auth=requests_ntlm.HttpNtlmAuth("user", "pass"),
        )
        assert True

Output running with pytest -v looks like this:

$ pytest -v
================================================= test session starts ==================================================
platform linux -- Python 3.8.6, pytest-6.2.2, py-1.10.0, pluggy-0.13.1 -- /home/marko/.pyenv/versions/3.8.6/envs/ntlm_auth/bin/python3.8
cachedir: .pytest_cache
rootdir: /mnt/c/Users/Mark Osbourne/ntlm_auth
collected 2 items

test_ntlm_auth.py::test_ntlm_auth PASSED                                                                         [ 50%]
test_ntlm_auth.py::test_ntlm_auth_with_vcr FAILED                                                                [100%]

======================================================= FAILURES =======================================================
_______________________________________________ test_ntlm_auth_with_vcr ________________________________________________

    def test_ntlm_auth_with_vcr():
        with vcr.use_cassette("/tmp/test_ntlm_auth.yaml"):
>           resp = requests.get(
                "http://ntlm.herokuapp.com/",
                auth=requests_ntlm.HttpNtlmAuth("user", "pass"),
            )

test_ntlm_auth.py:16:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/home/marko/.pyenv/versions/3.8.6/envs/ntlm_auth/lib/python3.8/site-packages/requests/api.py:76: in get
    return request('get', url, params=params, **kwargs)
/home/marko/.pyenv/versions/3.8.6/envs/ntlm_auth/lib/python3.8/site-packages/requests/api.py:61: in request
    return session.request(method=method, url=url, **kwargs)
/home/marko/.pyenv/versions/3.8.6/envs/ntlm_auth/lib/python3.8/site-packages/requests/sessions.py:542: in request
    resp = self.send(prep, **send_kwargs)
/home/marko/.pyenv/versions/3.8.6/envs/ntlm_auth/lib/python3.8/site-packages/requests/sessions.py:662: in send
    r = dispatch_hook('response', hooks, r, **kwargs)
/home/marko/.pyenv/versions/3.8.6/envs/ntlm_auth/lib/python3.8/site-packages/requests/hooks.py:31: in dispatch_hook
    _hook_data = hook(hook_data, **kwargs)
/home/marko/.pyenv/versions/3.8.6/envs/ntlm_auth/lib/python3.8/site-packages/requests_ntlm/requests_ntlm.py:146: in response_hook
    return self.retry_using_http_NTLM_auth(
/home/marko/.pyenv/versions/3.8.6/envs/ntlm_auth/lib/python3.8/site-packages/requests_ntlm/requests_ntlm.py:52: in retry_using_http_NTLM_auth
    server_certificate_hash = self._get_server_cert(response)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <requests_ntlm.requests_ntlm.HttpNtlmAuth object at 0x7f6bed88c5e0>, response = <Response [401]>

    def _get_server_cert(self, response):
        """
        Get the certificate at the request_url and return it as a hash. Will get the raw socket from the
        original response from the server. This socket is then checked if it is an SSL socket and then used to
        get the hash of the certificate. The certificate hash is then used with NTLMv2 authentication for
        Channel Binding Tokens support. If the raw object is not a urllib3 HTTPReponse (default with requests)
        then no certificate will be returned.

        :param response: The original 401 response from the server
        :return: The hash of the DER encoded certificate at the request_url or None if not a HTTPS endpoint
        """
        if self.send_cbt:
            certificate_hash = None
            raw_response = response.raw

            if isinstance(raw_response, HTTPResponse):
                if sys.version_info > (3, 0):
>                   socket = raw_response._fp.fp.raw._sock
E                   AttributeError: 'NoneType' object has no attribute 'raw'

/home/marko/.pyenv/versions/3.8.6/envs/ntlm_auth/lib/python3.8/site-packages/requests_ntlm/requests_ntlm.py:187: AttributeError
=============================================== short test summary info ================================================
FAILED test_ntlm_auth.py::test_ntlm_auth_with_vcr - AttributeError: 'NoneType' object has no attribute 'raw'
============================================= 1 failed, 1 passed in 0.71s ==============================================

gotmarko avatar Feb 26 '21 14:02 gotmarko

I found a workaround for this issue that works in my particular situation.

I use pytest, in conftest.py, I add the following monkeypatch:

from vcr.stubs import VCRHTTPResponse
from unittest import mock


def stubbed__setattr__(self, attribute, value):
    if attribute == 'fp':
        value = mock.Mock(raw=mock.Mock(_sock=None))
    rez = super(VCRHTTPResponse, self).__setattr__(attribute, value)
    return rez


VCRHTTPResponse.__setattr__ = stubbed__setattr__

benwah avatar Sep 09 '21 14:09 benwah