Skip to content

Commit

Permalink
PR raise exception on duplicate file, fix docs, fix logger name to us…
Browse files Browse the repository at this point in the history
…e inspect
  • Loading branch information
myuwono committed Feb 10, 2017
1 parent d2eaf01 commit 06e3520
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 27 deletions.
7 changes: 6 additions & 1 deletion src/main/python/covata/delta/crypto/cryptoservice.py
Expand Up @@ -53,7 +53,6 @@ def save(self, private_key, file_name):
>>> crypto_service.save(private_key, identity_id + ".signing.pem")
:param private_key: the private key object
:type private_key: :class:`RSAPrivateKey`
:param str file_name: the name of the .pem file to be written
Expand All @@ -69,6 +68,12 @@ def save(self, private_key, file_name):
self.logger.debug("creating directory %s", self.key_store_path)
os.makedirs(self.key_store_path)

if os.path.isfile(file_path):
msg = "Save failed: A key with name [{}] exists in keystore".format(
file_name)
self.logger.error(msg)
raise IOError(msg)

with open(file_path, 'w') as f:
self.logger.debug("Saving %s", file_name)
f.write(pem.decode(encoding='utf8'))
Expand Down
31 changes: 10 additions & 21 deletions src/main/python/covata/delta/crypto/signer.py
Expand Up @@ -59,16 +59,15 @@ class CVTSigner(AuthBase, LogMixin):

def __init__(self, crypto_service, identity_id):
"""
Create a Request Signer object to sign a
:class:`~requests.Request` object using
the CVT1 request signing scheme.
Creates a Request Signer object to sign a :class:`~requests.Request`
object using the CVT1 request signing scheme.
The :class:`~.CVTSigner` can be instantiated
directly using its constructor:
The :class:`~.CVTSigner` can be instantiated directly using its
constructor:
>>> signer = CVTSigner(crypto_service, authorizing_identity)
The :class:`~.CVTSigner` can also be instantiated indirectly via a
It can also be instantiated indirectly via a
:class:`~.CryptoService` object by calling
:func:`~covata.delta.crypto.CryptoService.signer`:
Expand All @@ -85,8 +84,9 @@ def __init__(self, crypto_service, identity_id):
... auth=crypto_service.signer(requestor_id))
>>> print(response.json())
It is also possible to invoke the call to manually attach
the appropriate headers to a :class:`~requests.PreparedRequest` object:
It is also possible to invoke the :func:`~.CVTSigner.__call__`
manually to attach the appropriate headers to a
:class:`~requests.PreparedRequest` object:
>>> prepared_request = request.prepare()
>>> signer(prepared_request)
Expand Down Expand Up @@ -118,17 +118,13 @@ def __get_auth_header(self, request):
self.logger.debug(string_to_sign)
signature = b64encode(self.__sign(string_to_sign)).decode('utf-8')

auth_header = "{algorithm} Identity={identity_id}, " \
"SignedHeaders={signed_headers}, Signature={signature}" \
return "{algorithm} Identity={identity_id}, " \
"SignedHeaders={signed_headers}, Signature={signature}" \
.format(algorithm=self.SIGNING_ALGORITHM,
identity_id=self.__identity_id,
signed_headers=signature_materials.signed_headers,
signature=signature)

self.logger.debug(auth_header)

return auth_header

def __sign(self, string_to_sign):
private_key = self.__crypto_service.load(
self.__identity_id + ".signing.pem")
Expand All @@ -147,13 +143,6 @@ def __get_hashed_payload(self, payload):

def __get_materials(self, request):
# type: (PreparedRequest) -> SignatureMaterial
"""
prepare the signature materials needed
:param request: the prepared request by
:return: the SignatureMaterial named tuple
:rtype: :class: `SignatureMaterial`
"""
# /master/identities/a123?key=an+arbitrary+value&key2=x
path = request.path_url.split("?")
uri = self.__encode_uri("/".join(path[0].split("/")[2:]))
Expand Down
23 changes: 22 additions & 1 deletion src/main/python/covata/delta/util.py
Expand Up @@ -13,11 +13,32 @@
# limitations under the License.

import logging
import inspect

__all__ = ["LogMixin"]


class LogMixin(object):
@property
def logger(self):
return logging.getLogger(self.__class__.__name__)
return logging.getLogger(self.__caller())

def __caller(self):
"""
Gets the name of the caller in {package}.{module}.{class} format
:return: the
"""
# type: () -> str
stack = inspect.stack()
if len(stack) < 2:
return ''

caller_frame = stack[2][0]
module = inspect.getmodule(caller_frame)
name = filter(lambda x: x is not None, [
module.__name__ if module else None,
self.__class__.__name__])

del caller_frame
return ".".join(name)
6 changes: 3 additions & 3 deletions src/unittest/python/conftest.py
Expand Up @@ -23,14 +23,14 @@
import covata.delta.api as api


@pytest.yield_fixture(scope="session")
@pytest.yield_fixture(scope="function")
def temp_directory():
directory = tempfile.mkdtemp()
yield directory
shutil.rmtree(directory)


@pytest.fixture(scope="session")
@pytest.fixture(scope="function")
def crypto_service(temp_directory):
return crypto.CryptoService(temp_directory, b"passphrase")

Expand Down Expand Up @@ -63,6 +63,6 @@ def mock_signer(mocker, crypto_service):
return_value=mocker.Mock())


@pytest.fixture(scope="session")
@pytest.fixture(scope="function")
def api_client(crypto_service):
return api.RequestsApiClient(crypto_service)
11 changes: 10 additions & 1 deletion src/unittest/python/test_crypto_service.py
Expand Up @@ -14,9 +14,10 @@

import base64

import pytest
import requests
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import requests


def test_generate_key_pairs(crypto_service):
Expand Down Expand Up @@ -66,3 +67,11 @@ def test_construct_signer(mocker, crypto_service, private_key):
json=dict(content="abcd"))
signer(r.prepare())
load.assert_called_once_with("mock.signing.pem")


def test_save__should__fail_when_key_exists(crypto_service, private_key):
crypto_service.save(private_key, "mock.pem")
with pytest.raises(IOError) as excinfo:
crypto_service.save(private_key, "mock.pem")
expected = "Save failed: A key with name [mock.pem] exists in keystore"
assert expected in str(excinfo.value)

0 comments on commit 06e3520

Please sign in to comment.