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

Conflict with requests-ntlm ('NoneType' object has no attribute 'raw') #87

Open
ubalklen opened this issue Jul 12, 2022 · 2 comments
Open

Comments

@ubalklen
Copy link

I'm getting a AttributeError: 'NoneType' object has no attribute 'raw' when trying to record tests that access sites that use NTLM authentication via the requests plugin requests-ntlm.

I don't know how to provide a reproducible sample without actually having a NTLM site, but here is a minimal code showing what is happening:

# foo.py
import pytest
import requests
from requests_ntlm import HttpNtlmAuth

pytestmark = pytest.mark.vcr

def login(user, pwd):
    session = requests.Session()
    session.auth = HttpNtlmAuth(user, pwd)

    return session

def test_login():
    s = login("foo", "bar")
    s.get("https://my-ntlm-site.com")

If I turn pytest-recording off, the test works:

> pytest foo.py -p no:recording 
===================================================================================== test session starts =====================================================================================
platform win32 -- Python 3.9.11, pytest-7.1.1, pluggy-1.0.0
rootdir: C:\Users\user\code
plugins: cov-3.0.0
collected 1 item

foo.py .                                                                                                                                                                                 [100%] 

====================================================================================== warnings summary ======================================================================================= 
foo.py:5
  C:\Users\user\code\foo.py:5: PytestUnknownMarkWarning: Unknown pytest.mark.vcr - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    pytestmark = pytest.mark.vcr

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================================================================ 1 passed, 1 warning in 0.52s =================================================================================

But if I turn it on:

> pytest foo.py --record-mode=all
===================================================================================== test session starts =====================================================================================
platform win32 -- Python 3.9.11, pytest-7.1.1, pluggy-1.0.0
rootdir: C:\Users\user\code
plugins: cov-3.0.0, recording-0.12.1
collected 1 item

foo.py F                                                                                                                                                                                 [100%]

========================================================================================== FAILURES ===========================================================================================
_________________________________________________________________________________________ test_login __________________________________________________________________________________________

    def test_login():
        s = login("foo", "bar")
>       s.get("https://my-ntlm-site.com")

foo.py:15: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\..\Miniconda3\envs\myenv\lib\site-packages\requests\sessions.py:542: in get
    return self.request('GET', url, **kwargs)
..\..\Miniconda3\envs\myenv\lib\site-packages\requests\sessions.py:529: in request
    resp = self.send(prep, **send_kwargs)
..\..\Miniconda3\envs\myenv\lib\site-packages\requests\sessions.py:652: in send
    r = dispatch_hook('response', hooks, r, **kwargs)
..\..\Miniconda3\envs\myenv\lib\site-packages\requests\hooks.py:31: in dispatch_hook
    _hook_data = hook(hook_data, **kwargs)
..\..\Miniconda3\envs\myenv\lib\site-packages\requests_ntlm\requests_ntlm.py:146: in response_hook
    return self.retry_using_http_NTLM_auth(
..\..\Miniconda3\envs\myenv\lib\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 0x000001B353EC4D00>, 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'

..\..\Miniconda3\envs\myenv\lib\site-packages\requests_ntlm\requests_ntlm.py:187: AttributeError
=================================================================================== short test summary info =================================================================================== 
FAILED foo.py::test_login - AttributeError: 'NoneType' object has no attribute 'raw'
====================================================================================== 1 failed in 1.45s ====================================================================================== 
@ubalklen
Copy link
Author

ubalklen commented Jul 12, 2022

I forgot to mention that a cassette do get recorded after the failed test, but with a 401 UNAUTHORIZED in its body, meaning there was an issue during the NTLM authentication.

@Stranger6667
Copy link
Collaborator

Hey! Sorry for the delay.

I think the core reason is how VCR.py mocks requests. I assume it doesn't create raw response (from urllib3) when it takes response from a cassette. I.e. it should be fixed there, but I'd be happy to keep this issue open to track the progress.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants