Skip to content

Commit

Permalink
flatten package structure, deleted CVTSigner class (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
myuwono committed Feb 20, 2017
1 parent 3c3405b commit 7e4e39f
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 436 deletions.
6 changes: 4 additions & 2 deletions src/main/python/covata/delta/__init__.py
Expand Up @@ -14,7 +14,9 @@

from __future__ import absolute_import

from .interfaces import DeltaApiClient, DeltaKeyStore
from .apiclient import ApiClient
from .keystore import DeltaKeyStore
from .keystore import FileSystemKeyStore
from .utils import LogMixin

__all__ = ["DeltaApiClient", "DeltaKeyStore", "LogMixin"]
__all__ = ["ApiClient", "FileSystemKeyStore", "LogMixin", "DeltaKeyStore"]
20 changes: 0 additions & 20 deletions src/main/python/covata/delta/api/__init__.py

This file was deleted.

Expand Up @@ -13,17 +13,46 @@
# limitations under the License.

from __future__ import absolute_import
from base64 import b64encode, b64decode

import requests

from covata.delta import DeltaApiClient, LogMixin, crypto
from covata.delta.api.signer import CVTSigner
from base64 import b64encode, b64decode
from requests.auth import AuthBase
from covata.delta import utils
from covata.delta import crypto
from covata.delta import signer


class ApiClient(utils.LogMixin):

DELTA_URL = 'https://delta.covata.io/v1' # type: str
RESOURCE_IDENTITIES = '/identities' # type: str
RESOURCE_SECRETS = '/secrets' # type: str

def __init__(self, keystore):
"""
Constructs a new Delta API client with the given configuration.
:param keystore: the DeltaKeyStore object
:type keystore: :class:`~covata.delta.DeltaKeyStore`
"""
self.__keystore = keystore # type: DeltaKeyStore

@property
def keystore(self):
return self.__keystore

class RequestsApiClient(DeltaApiClient, LogMixin):
def register_identity(self, external_id=None, metadata=None):
"""
Creates a new identity in Delta with the provided metadata
and external id.
:param external_id: the external id to associate with the identity
:param metadata: the metadata to associate with the identity
:return: the id of the newly created identity
:type external_id: str | None
:type metadata: dict[str, str] | None
:rtype: str
"""
private_signing_key = crypto.generate_private_key()
private_encryption_key = crypto.generate_private_key()

Expand All @@ -49,6 +78,14 @@ def register_identity(self, external_id=None, metadata=None):
return identity_id

def get_identity(self, requestor_id, identity_id):
"""
Gets the identity matching the given identity id.
:param str requestor_id: the authenticating identity id
:param str identity_id: the identity id to retrieve
:return: the retrieved identity
:rtype: dict[str, any]
"""
response = requests.get(
url="{base_url}{resource}/{identity_id}".format(
base_url=self.DELTA_URL,
Expand All @@ -60,6 +97,17 @@ def get_identity(self, requestor_id, identity_id):
return identity

def create_secret(self, requestor_id, content, encryption_details):
"""
Creates a new secret in Delta. The key used for encryption should
be encrypted with the key of the authenticating identity.
:param str requestor_id: the authenticating identity id
:param bytes content: the contents of the secret
:param encryption_details: the encryption details
:type encryption_details: dict[str, bytes]
:return: the created base secret
:rtype: dict[str, str]
"""
content_b64 = b64encode(content).decode('utf-8')
encryption_details_b64 = dict(
(k, b64encode(v).decode('utf-8'))
Expand All @@ -80,6 +128,22 @@ def create_secret(self, requestor_id, content, encryption_details):

def share_secret(self, requestor_id, content, encryption_details,
base_secret_id, rsa_key_owner_id):
"""
Shares the base secret with the specified target RSA key owner. The
contents must be encrypted with the public encryption key of the
RSA key owner, and the encrypted key and initialisation vector must
be provided. This call will result in a new derived secret being created
and returned as a response.
:param str requestor_id: the authenticating identity id
:param bytes content: the contents of the secret
:param encryption_details: the encryption details
:type encryption_details: dict[str, bytes]
:param str base_secret_id: the id of the base secret
:param str rsa_key_owner_id: the id of the rsa key owner
:return: the created derived secret
:rtype: dict[str, str]
"""
content_b64 = b64encode(content).decode('utf-8')
encryption_details_b64 = dict(
(k, b64encode(v).decode('utf-8'))
Expand All @@ -100,18 +164,54 @@ def share_secret(self, requestor_id, content, encryption_details,
response.raise_for_status()
return response.json()

def get_secret_content(self, requestor_id, secret_id):
response = requests.get(
url="{base_url}{resource}/{secret_id}/content".format(
def delete_secret(self, requestor_id, secret_id):
"""
Deletes the secret with the given secret id.
:param str requestor_id: the authenticating identity id
:param str secret_id: the secret id to be deleted
"""
response = requests.delete(
url="{base_url}{resource}/{secret_id}".format(
base_url=self.DELTA_URL,
resource=self.RESOURCE_SECRETS,
secret_id=secret_id),
auth=self.signer(requestor_id))
response.raise_for_status()

def get_secret(self, requestor_id, secret_id):
"""
Gets the given secret. This does not include the metadata and contents,
they need to be made as separate requests,
:func:`~.DeltaApiClient.get_secret_metadata`
and :func:`~.DeltaApiClient.get_secret_content` respectively.
:param str requestor_id: the authenticating identity id
:param str secret_id: the secret id to be retrieved
:return: the retrieved secret
:rtype: dict[str, any]
"""
response = requests.get(
url="{base_url}{resource}/{secret_id}".format(
base_url=self.DELTA_URL,
resource=self.RESOURCE_SECRETS,
secret_id=secret_id),
auth=self.signer(requestor_id))
response.raise_for_status()
return b64decode(response.json())
secret = response.json()
for k, v in secret["encryptionDetails"].items():
secret["encryptionDetails"][k] = b64decode(v)
return secret

def get_secret_metadata(self, requestor_id, secret_id):
"""
Gets the metadata key and value pairs for the given secret.
:param str requestor_id: the authenticating identity id
:param str secret_id: the secret id to be retrieved
:return: the retrieved secret metadata dictionary and version tuple
:rtype: (dict[str, str], int)
"""
response = requests.get(
url="{base_url}{resource}/{secret_id}/metadata".format(
base_url=self.DELTA_URL,
Expand All @@ -124,30 +224,42 @@ def get_secret_metadata(self, requestor_id, secret_id):
version = int(response.headers["ETag"])
return metadata, version

def get_secret(self, requestor_id, secret_id):
def get_secret_content(self, requestor_id, secret_id):
"""
Gets the contents of the given secret.
:param str requestor_id: the authenticating identity id
:param str secret_id: the secret id to be retrieved
:return: the retrieved secret
:rtype: bytes
"""
response = requests.get(
url="{base_url}{resource}/{secret_id}".format(
url="{base_url}{resource}/{secret_id}/content".format(
base_url=self.DELTA_URL,
resource=self.RESOURCE_SECRETS,
secret_id=secret_id),
auth=self.signer(requestor_id))
response.raise_for_status()
secret = response.json()
for k, v in secret["encryptionDetails"].items():
secret["encryptionDetails"][k] = b64decode(v)
return secret

def delete_secret(self, requestor_id, secret_id):
response = requests.delete(
url="{base_url}{resource}/{secret_id}".format(
base_url=self.DELTA_URL,
resource=self.RESOURCE_SECRETS,
secret_id=secret_id),
auth=self.signer(requestor_id))
response.raise_for_status()
return b64decode(response.json())

def update_secret_metadata(self,
requestor_id, secret_id, metadata, version):
requestor_id,
secret_id,
metadata,
version):
"""
Updates the metadata of the given secret given the version number.
The version of a secret's metadata can be obtained by calling
:func:`~.DeltaApiClient.get_secret_content`.
A newly created base secret has a metadata version of 1.
:param str requestor_id: the authenticating identity id
:param str secret_id: the secret id to be updated
:param metadata: metadata dictionary
:type metadata: dict[str, str]
:param int version: metadata version, required for optimistic locking
"""
response = requests.put(
url="{base_url}{resource}/{secret_id}/metadata".format(
base_url=self.DELTA_URL,
Expand All @@ -161,8 +273,23 @@ def update_secret_metadata(self,

response.raise_for_status()

def update_identity_metadata(self, requestor_id, identity_id, metadata,
def update_identity_metadata(self,
requestor_id,
identity_id,
metadata,
version):
"""
Updates the metadata of the given identity given the version number.
The version of an identity's metadata can be obtained by calling
:func:`~.DeltaApiClient.get_identity`.
An identity has an initial metadata version of 1.
:param str requestor_id: the authenticating identity id
:param str identity_id: the identity id to be updated
:param metadata: metadata dictionary
:type metadata: dict[str, str]
:param int version: metadata version, required for optimistic locking
"""
response = requests.put(
url="{base_url}{resource}/{identity_id}".format(
base_url=self.DELTA_URL,
Expand All @@ -186,62 +313,14 @@ def signer(self, identity_id):
:return: the :class:`~.RequestsCVTSigner` object
:rtype: :class:`~.RequestsCVTSigner`
"""
return RequestsCVTSigner(self.keystore, identity_id)


class RequestsCVTSigner(AuthBase, CVTSigner, LogMixin):
def __init__(self, keystore, identity_id):
"""
Creates a Request Signer object to sign a :class:`~requests.Request`
object using the CVT1 request signing scheme.
The :class:`~.RequestsCVTSigner` can be instantiated directly using its
constructor:
>>> signer = RequestsCVTSigner(keystore, authorizing_identity)
It can also be instantiated indirectly via a
:class:`~covata.delta.api.RequestsApiClient` object by calling
:func:`~covata.delta.api.RequestsApiClient.signer`:
>>> signer = api_client.signer(authorizing_identity)
Example usage for retrieving an identity:
>>> import requests
>>> api_client = RequestsApiClient(keystore)
>>> signer = api_client.signer(authorizing_identity)
>>> response = requests.get(
... url="{base_url}{resource}{identity_id}".format(
... base_url="https://delta.covata.io/v1",
... resource="/identities/",
... identity_id="e5fa4059-24c0-42a8-af9a-fe7280b43256"),
... auth=signer)
>>> print(response.json())
It is also possible to invoke the :func:`~.RequestsSigner.__call__`
manually to attach the appropriate headers to a
:class:`~requests.PreparedRequest` object:
>>> from requests import Request
>>> request = Request('GET', url)
>>> prepared_request = request.prepare()
>>> signer(prepared_request)
>>> "Authorization" in request.headers
True
:param keystore: The :class:`~covata.delta.DeltaKeyStore` object
:type keystore: :class:`~covata.delta.DeltaKeyStore`
:param str identity_id: the authorizing identity id
"""
super(RequestsCVTSigner, self).__init__(keystore)
self.__identity_id = identity_id

def __call__(self, r):
r.headers = self.get_signed_headers(identity_id=self.__identity_id,
method=r.method,
url=r.url,
headers=r.headers,
payload=r.body)
return r
def sign_request(r):
signing_key = self.keystore.get_private_signing_key(identity_id)
r.headers = signer.get_updated_headers(
identity_id=identity_id,
method=r.method,
url=r.url,
headers=r.headers,
payload=r.body,
private_signing_key=signing_key)
return r
return sign_request
File renamed without changes.
28 changes: 0 additions & 28 deletions src/main/python/covata/delta/crypto/__init__.py

This file was deleted.

0 comments on commit 7e4e39f

Please sign in to comment.