-
Notifications
You must be signed in to change notification settings - Fork 184
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
Add pkcs12 support #234
Add pkcs12 support #234
Conversation
Allow users for whom decrypting a .p12/pfx certificate into a .pem file is not desireable or feasible to pass a pkcs12 certificate as part of their request.
rather than import from ssl use _ssl in order to ensure pulling constants properly from OpnenSSL
…12.0 Due to the use of the PyOpenSSlContext object, introduced in urllib3 version 1.19, use of the Pkcs12Adapter class can not be used by versions of requests prior to 2.12.0.
I think this looks okay to me (There are some style nits but those aren't as important to me in the context of this PR). I'm asking someone else with a bit more experience around OpenSSL and Pkcs12 to review this to make sure I'm not missing something crucial. |
Hey, providing some feedback at @sigmavirus24's request.
|
@alex thanks for the feedback, I really appreciate it.
|
|
@alex you should be more like this?
|
Something like that, yeah! And maybe it should even be on the default |
I would agree, but that is a question for @sigmavirus24 because then we would need to move from the toolbelt to requests/requests since that is where |
I'd be happy to have that in the toolbelt today. And work towards getting it exempted from requests' feature-freeze for sometime in the future if we can show there's a lot of usage of it. |
Per @alex's comment, widen the adapter to accept a generic X.509 certificate and private key as byte arrays regardless of serialization or encoding method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, @alex could you give this a second pass?
docs/adapters.rst
Outdated
@@ -21,7 +21,7 @@ with requests. The transport adapters are all kept in | |||
|
|||
- :class:`requests_toolbelt.adapters.host_header_ssl.HostHeaderSSLAdapter` | |||
|
|||
- :class:`requests_toolbelt.adapters.pkcs12_adapter.Pkcs12Adapter` | |||
- :class:`requests_toolbelt.adapters.x509_adapter.X509Adapter` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pointed out to me the module name. The other adapter modules don't have _adapter
in the name. Would you be comfortable renaming the module to be x509
so this reads as requests_toolbelt.adapters.x509.X509Adapter
?
docs/adapters.rst
Outdated
Example usage: | ||
|
||
.. code-block:: python | ||
|
||
from requests_toolbelt.adapters import Pkcs12Adapter | ||
import requests | ||
from requests_toolbelt.adapters import X509Adapter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be:
from requests_toolbelt.adapters import X509Adapter | |
from requests_toolbelt.adapters.x509 import X509Adapter |
requests_toolbelt/__init__.py
Outdated
@@ -28,7 +28,7 @@ | |||
__all__ = [ | |||
'GuessAuth', 'MultipartEncoder', 'MultipartEncoderMonitor', | |||
'MultipartDecoder', 'SSLAdapter', 'SourceAddressAdapter', | |||
'Pkcs12Adapter', 'StreamingIterator', 'user_agent', | |||
'X509Adapter', 'StreamingIterator', 'user_agent', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we stopped adding new things to the top level exports purposefully. We've instead started documenting importing the class from the fully-qualified path to reduce the impact on importing this module (that way people only import what they need and don't incur the costs of the rest of the library). Please revert this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This especially important since we're importing OpenSSL and cryptography in the adapter's module and not everyone will have that installed. By default importing this will make that a hard requirement rather than an optional one.
from .. import exceptions as exc | ||
|
||
try: | ||
from _ssl import PROTOCOL_TLS as PROTOCOL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious what the reasoning is to use _ssl
here instead of ssl
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i used _ssl
because i didn't need any of the actual implementation portions of ssl, just some of the constants that are defined in _ssl
and passed through ssl
. It also solved an issue i was having trying to import directly from ssl
that only occurred in the tox py27 environment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm surprised that you ran into issues with importing ssl
on Py27. Would you be willing to add a comment explaining that? I just don't want to come back to this in the future, change the import and break something for someone.
What version of Python 2.7 are you using by the way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a comment explaining that. I'm using 2.7.15, but it actually happened under tox in 2.7.14. I tried several things to try to correct it, but the switch to using _ssl was the only thing that worked on the 2.7.x and 3.x lines.
:param pool_block: Whether the connection pool should block for | ||
connections. | ||
|
||
:param \**kwargs: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From here to the usage section doesn't follow the documentation style of the library, let's change this to:
:param \**kwargs: | |
:param bytes cert_bytes: | |
(your description here) | |
:param bytes pk_bytes: | |
(your description here) | |
:param password: | |
(your description here) | |
:param encoding: | |
(your description here sans the type) | |
:type encoding: | |
:class:`cryptography.hazmat.primitives.serialization.Encoding` |
def check_cert_dates(cert): | ||
"""Verify that the supplied client cert is not invalid.""" | ||
|
||
if ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we did
if ( | |
now = datetime.datetime.utcnow() | |
if cert.not_valid_after < now or cert.not_valid_before > now: |
tests/test_x509_adapter.py
Outdated
|
||
REQUESTS_SUPPORTS_SSL_CONTEXT = requests.__build__ >= 0x021200 | ||
|
||
@pytest.mark.skipif(not REQUESTS_SUPPORTS_SSL_CONTEXT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's also add 1 test that is skipped that looks vaguely like:
from requests_toolbelt import exceptions as exc
@pytest.mark.skipif(REQUESTS_SUPPORTS_SSL_CONTEXT, reason="Will not raise exc")
def test_requires_new_enough_requests():
with pytest.raises(exc.VersionMismatchError):
X509Adapter()
This will ensure we don't regress the fact that we check the version.
Co-Authored-By: rashley-iqt <37374243+rashley-iqt@users.noreply.github.com>
Co-Authored-By: rashley-iqt <37374243+rashley-iqt@users.noreply.github.com>
@sigmavirus24 i've got the changes you requested mostly implemented, but I am currently struggling with betamax. When i perform my tests locally and can access the server i am using for testing the tests pass. However, when I disconnect the network and rely on the cassette file the tests fail. I notice that the |
Updates to reflect changes requested by @sigmavirus24
@sigmavirus24 I figured it out and everything is passing now as well as all of the requested changes being made. |
0c5357b
to
e6339cb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple things remaining if you wouldn't mind cleaning them up. Otherwise this looks good
requests_toolbelt/adapters/x509.py
Outdated
:param \**kwargs: | ||
see below | ||
|
||
:Keyword Arguments: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems my request about this documentation being corrected was lost.
requests_toolbelt/adapters/x509.py
Outdated
def check_cert_dates(cert): | ||
"""Verify that the supplied client cert is not invalid.""" | ||
|
||
if ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also seems my request to import the datetime module and find utcnow()
was also lost.
connections. | ||
|
||
:param bytes cert_bytes: | ||
bytes object containing contents of a cryptography.x509Certificate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what happened there are two files here, the x509_adapter
and x509
are not in sync properly and one has the changes I wanted while the other has the module name i wanted. Can you reconcile these please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, something went weird with the rename and merge. I've corrected this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to apologize :) Thanks for fixing it up
Alex gave this an informal 👍 on IRC, so I'm going to merge it. Thanks @rashley-iqt for following through and tackling this. |
thank you guys for helping me get it ready for use. |
@sigmavirus24 do you have any idea when the next release of the toolbelt is scheduled for? |
There isn't a schedule. We need to put together release notes though before we can release. If you want to send a PR with those notes then that can help accelerate this as the weather where I am should keep me outside shoveling snow probably through the end of my life 😜 |
Hey folks, I suspect this might have introduced a breaking change into this project. We're installing this package as a dependency of Full stack trace: Traceback (most recent call last):
File "/usr/local/bin/twine", line 6, in <module>
from twine.__main__ import main
File "/usr/local/lib/python3.6/site-packages/twine/__main__.py", line 23, in <module>
from twine.cli import dispatch
File "/usr/local/lib/python3.6/site-packages/twine/cli.py", line 23, in <module>
import requests_toolbelt
File "/usr/local/lib/python3.6/site-packages/requests_toolbelt/__init__.py", line 12, in <module>
from .adapters import SSLAdapter, SourceAddressAdapter
File "/usr/local/lib/python3.6/site-packages/requests_toolbelt/adapters/__init__.py", line 12, in <module>
from .ssl import SSLAdapter
File "/usr/local/lib/python3.6/site-packages/requests_toolbelt/adapters/ssl.py", line 16, in <module>
from .._compat import poolmanager
File "/usr/local/lib/python3.6/site-packages/requests_toolbelt/_compat.py", line 59, in <module>
from urllib3.contrib.pyopenssl import PyOpenSSLContext
File "/usr/local/lib/python3.6/site-packages/urllib3/contrib/pyopenssl.py", line 46, in <module>
import OpenSSL.SSL
ModuleNotFoundError: No module named 'OpenSSL' |
An adapter to be used in cases where a user needs to authenticate to a service using a .p12/.pfx certificate and it is not desirable or feasible to first convert that certificate into a .pem format. Born out of a use case in which the existence of temporary files containing cert data were deemed too great a risk.