Skip to content

Commit

Permalink
Replace PyCrpyto dependency with cryptography.
Browse files Browse the repository at this point in the history
Fix up tests.
Fix up pypi badges since pypipins is seemingly permanently down.
  • Loading branch information
Jeff-Meadows committed Jul 7, 2015
1 parent 423fa2a commit 46b0e6e
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 16 deletions.
10 changes: 8 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ box-python-sdk
:target: http://box-python-sdk.readthedocs.org/en/latest
:alt: Documentation Status

.. image:: https://pypip.in/v/boxsdk/badge.png
.. image:: https://img.shields.io/pypi/v/boxsdk.svg
:target: https://pypi.python.org/pypi/boxsdk

.. image:: https://pypip.in/d/boxsdk/badge.png
.. image:: https://img.shields.io/pypi/dm/boxsdk.svg
:target: https://pypi.python.org/pypi/boxsdk


Expand Down Expand Up @@ -212,6 +212,12 @@ Box Developer Edition
The Python SDK supports your
`Box Developer Edition <https://developers.box.com/developer-edition/>`__ applications.

Developer Edition support requires some extra dependencies. To get them, simply

.. code-block:: console
pip install boxsdk[jwt]
Instead of instantiating your `Client` with an instance of `OAuth2`,
instead use an instance of `JWTAuth`.

Expand Down
10 changes: 8 additions & 2 deletions boxsdk/auth/jwt_auth.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# coding: utf-8

from __future__ import unicode_literals
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from datetime import datetime, timedelta
import jwt
from Crypto.PublicKey.RSA import importKey as import_key
import random
from six import binary_type
import string
from .oauth2 import OAuth2
from boxsdk.util.compat import total_seconds
Expand Down Expand Up @@ -87,7 +89,11 @@ def __init__(
network_layer=network_layer,
)
with open(rsa_private_key_file_sys_path) as key_file:
self._rsa_private_key = import_key(key_file.read(), rsa_private_key_passphrase).exportKey('PEM')
self._rsa_private_key = serialization.load_pem_private_key(
key_file.read(),
password=rsa_private_key_passphrase and binary_type(rsa_private_key_passphrase),
backend=default_backend(),
)
self._enterprise_token = enterprise_id
self._jwt_algorithm = jwt_algorithm
self._user_id = None
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cryptography>=0.9.2
enum34>=1.0.4
ordereddict>=1.1
pycrypto>=2.6.1
pyjwt>=1.3.0
requests>=2.4.3
six >= 1.4.0
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def main():
url='http://opensource.box.com',
packages=find_packages(exclude=['demo', 'docs', 'test']),
install_requires=install_requires,
extras_require={'jwt': ['pycrypto>=2.6.1', 'pyjwt>=1.3.0']},
extras_require={'jwt': ['cryptography>=0.9.2', 'pyjwt>=1.3.0']},
tests_require=['pytest', 'pytest-xdist', 'mock', 'sqlalchemy', 'bottle', 'jsonpatch'],
cmdclass={'test': PyTest},
classifiers=CLASSIFIERS,
Expand Down
52 changes: 42 additions & 10 deletions test/unit/auth/test_jwt_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

from __future__ import unicode_literals
from contextlib import contextmanager
from cryptography.hazmat.backends import default_backend
from datetime import datetime, timedelta
import json
from mock import Mock, mock_open, patch, sentinel
import pytest
import random
from six import binary_type
import string
from boxsdk.auth.jwt_auth import JWTAuth
from boxsdk.config import API
Expand All @@ -24,6 +26,11 @@ def jwt_algorithm(request):
return request.param


@pytest.fixture(params=(None, 'strong_password'))
def rsa_passphrase(request):
return request.param


@pytest.fixture(scope='module')
def successful_token_response(successful_token_mock, successful_token_json_response):
# pylint:disable=redefined-outer-name
Expand All @@ -37,7 +44,13 @@ def successful_token_response(successful_token_mock, successful_token_json_respo


@contextmanager
def jwt_auth_init_mocks(mock_network_layer, successful_token_response, jwt_algorithm, enterprise_token=None):
def jwt_auth_init_mocks(
mock_network_layer,
successful_token_response,
jwt_algorithm,
rsa_passphrase,
enterprise_token=None,
):
# pylint:disable=redefined-outer-name
fake_client_id = 'fake_client_id'
fake_client_secret = 'fake_client_secret'
Expand All @@ -54,24 +67,27 @@ def jwt_auth_init_mocks(mock_network_layer, successful_token_response, jwt_algor
mock_network_layer.request.return_value = successful_token_response
key_file = mock_open()
with patch('boxsdk.auth.jwt_auth.open', key_file, create=True) as jwt_auth_open:
with patch('boxsdk.auth.jwt_auth.import_key') as import_key:
with patch('cryptography.hazmat.primitives.serialization.load_pem_private_key') as load_pem_private_key:
oauth = JWTAuth(
client_id=fake_client_id,
client_secret=fake_client_secret,
enterprise_id=enterprise_token,
rsa_private_key_file_sys_path=sentinel.rsa_path,
rsa_private_key_passphrase=sentinel.rsa_passphrase,
rsa_private_key_passphrase=rsa_passphrase,
network_layer=mock_network_layer,
box_device_name='my_awesome_device',
jwt_algorithm=jwt_algorithm,
)

jwt_auth_open.assert_called_once_with(sentinel.rsa_path)
key_file.return_value.read.assert_called_once_with()
import_key.assert_called_once_with(key_file.return_value.read.return_value, sentinel.rsa_passphrase)
import_key.return_value.exportKey.assert_called_once_with('PEM')
load_pem_private_key.assert_called_once_with(
key_file.return_value.read.return_value,
password=rsa_passphrase and binary_type(rsa_passphrase),
backend=default_backend(),
)

yield oauth, assertion, fake_client_id, import_key.return_value.exportKey.return_value
yield oauth, assertion, fake_client_id, load_pem_private_key.return_value

mock_network_layer.request.assert_called_once_with(
'POST',
Expand Down Expand Up @@ -121,10 +137,11 @@ def test_authenticate_app_user_sends_post_request_with_correct_params(
successful_token_response,
jti_length,
jwt_algorithm,
rsa_passphrase,
):
# pylint:disable=redefined-outer-name
fake_user_id = 'fake_user_id'
with jwt_auth_init_mocks(mock_network_layer, successful_token_response, jwt_algorithm) as params:
with jwt_auth_init_mocks(mock_network_layer, successful_token_response, jwt_algorithm, rsa_passphrase) as params:
with jwt_auth_auth_mocks(jti_length, jwt_algorithm, fake_user_id, 'user', *params) as oauth:
oauth.authenticate_app_user(User(None, fake_user_id))

Expand All @@ -134,10 +151,17 @@ def test_authenticate_instance_sends_post_request_with_correct_params(
successful_token_response,
jti_length,
jwt_algorithm,
rsa_passphrase,
):
# pylint:disable=redefined-outer-name
enterprise_token = 'fake_enterprise_token'
with jwt_auth_init_mocks(mock_network_layer, successful_token_response, jwt_algorithm, enterprise_token) as params:
with jwt_auth_init_mocks(
mock_network_layer,
successful_token_response,
jwt_algorithm,
rsa_passphrase,
enterprise_token,
) as params:
with jwt_auth_auth_mocks(jti_length, jwt_algorithm, enterprise_token, 'enterprise', *params) as oauth:
oauth.authenticate_instance()

Expand All @@ -147,10 +171,11 @@ def test_refresh_app_user_sends_post_request_with_correct_params(
successful_token_response,
jti_length,
jwt_algorithm,
rsa_passphrase,
):
# pylint:disable=redefined-outer-name
fake_user_id = 'fake_user_id'
with jwt_auth_init_mocks(mock_network_layer, successful_token_response, jwt_algorithm) as params:
with jwt_auth_init_mocks(mock_network_layer, successful_token_response, jwt_algorithm, rsa_passphrase) as params:
with jwt_auth_auth_mocks(jti_length, jwt_algorithm, fake_user_id, 'user', *params) as oauth:
oauth._user_id = fake_user_id # pylint:disable=protected-access
oauth.refresh(None)
Expand All @@ -161,9 +186,16 @@ def test_refresh_instance_sends_post_request_with_correct_params(
successful_token_response,
jti_length,
jwt_algorithm,
rsa_passphrase,
):
# pylint:disable=redefined-outer-name
enterprise_token = 'fake_enterprise_token'
with jwt_auth_init_mocks(mock_network_layer, successful_token_response, jwt_algorithm, enterprise_token) as params:
with jwt_auth_init_mocks(
mock_network_layer,
successful_token_response,
jwt_algorithm,
rsa_passphrase,
enterprise_token,
) as params:
with jwt_auth_auth_mocks(jti_length, jwt_algorithm, enterprise_token, 'enterprise', *params) as oauth:
oauth.refresh(None)

0 comments on commit 46b0e6e

Please sign in to comment.