Skip to content

Commit cc06c2c

Browse files
committed
consolidate crypto config building logic across encrypted Client/Resource/Table
1 parent 63a3128 commit cc06c2c

File tree

4 files changed

+92
-95
lines changed

4 files changed

+92
-95
lines changed

src/dynamodb_encryption_sdk/encrypted/client.py

Lines changed: 19 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,20 @@
1717
import botocore.client
1818

1919
from dynamodb_encryption_sdk.internal.utils import (
20+
crypto_config_from_cache, crypto_config_from_kwargs,
2021
decrypt_batch_get_item, decrypt_get_item, decrypt_multi_get,
2122
encrypt_batch_write_item, encrypt_put_item, TableInfoCache
2223
)
2324
from dynamodb_encryption_sdk.material_providers import CryptographicMaterialsProvider
24-
from dynamodb_encryption_sdk.structures import AttributeActions, EncryptionContext
25-
from . import CryptoConfig
25+
from dynamodb_encryption_sdk.structures import AttributeActions
2626
from .item import decrypt_dynamodb_item, encrypt_dynamodb_item
2727

2828
__all__ = ('EncryptedClient',)
2929

3030

3131
@attr.s
3232
class EncryptedClient(object):
33-
# pylint: disable=too-few-public-methods
33+
# pylint: disable=too-few-public-methods,too-many-instance-attributes
3434
"""High-level helper class to provide a familiar interface to encrypted tables.
3535
3636
.. note::
@@ -68,40 +68,50 @@ def __attrs_post_init__(self):
6868
client=self._client,
6969
auto_refresh_table_indexes=self._auto_refresh_table_indexes
7070
)
71+
self._table_crypto_config = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
72+
crypto_config_from_cache,
73+
materials_provider=self._materials_provider,
74+
attribute_actions=self._attribute_actions,
75+
table_info_cache=self._table_info_cache
76+
)
77+
self._item_crypto_config = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
78+
crypto_config_from_kwargs,
79+
fallback=self._table_crypto_config
80+
)
7181
self.get_item = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
7282
decrypt_get_item,
7383
decrypt_method=decrypt_dynamodb_item,
74-
crypto_config_method=self._table_crypto_config,
84+
crypto_config_method=self._item_crypto_config,
7585
read_method=self._client.get_item
7686
)
7787
self.put_item = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
7888
encrypt_put_item,
7989
encrypt_method=encrypt_dynamodb_item,
80-
crypto_config_method=self._table_crypto_config,
90+
crypto_config_method=self._item_crypto_config,
8191
write_method=self._client.put_item
8292
)
8393
self.query = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
8494
decrypt_multi_get,
8595
decrypt_method=decrypt_dynamodb_item,
86-
crypto_config_method=self._table_crypto_config,
96+
crypto_config_method=self._item_crypto_config,
8797
read_method=self._client.query
8898
)
8999
self.scan = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
90100
decrypt_multi_get,
91101
decrypt_method=decrypt_dynamodb_item,
92-
crypto_config_method=self._table_crypto_config,
102+
crypto_config_method=self._item_crypto_config,
93103
read_method=self._client.scan
94104
)
95105
self.batch_get_item = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
96106
decrypt_batch_get_item,
97107
decrypt_method=decrypt_dynamodb_item,
98-
crypto_config_method=self._batch_crypto_config,
108+
crypto_config_method=self._table_crypto_config,
99109
read_method=self._client.batch_get_item
100110
)
101111
self.batch_write_item = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
102112
encrypt_batch_write_item,
103113
encrypt_method=encrypt_dynamodb_item,
104-
crypto_config_method=self._batch_crypto_config,
114+
crypto_config_method=self._table_crypto_config,
105115
write_method=self._client.batch_write_item
106116
)
107117

@@ -115,47 +125,6 @@ def __getattr__(self, name):
115125
"""
116126
return getattr(self._client, name)
117127

118-
def _crypto_config(self, table_name, **kwargs):
119-
"""Pull all encryption-specific parameters from the request and use them to build a crypto config.
120-
121-
:returns: crypto config and updated kwargs
122-
:rtype: dynamodb_encryption_sdk.encrypted.CryptoConfig and dict
123-
"""
124-
crypto_config = kwargs.pop('crypto_config', None)
125-
126-
if crypto_config is not None:
127-
return crypto_config, kwargs
128-
129-
table_info = self._table_info_cache.table_info(table_name)
130-
131-
attribute_actions = self._attribute_actions.copy()
132-
attribute_actions.set_index_keys(*table_info.protected_index_keys())
133-
134-
crypto_config = CryptoConfig(
135-
materials_provider=self._materials_provider,
136-
encryption_context=EncryptionContext(**table_info.encryption_context_values),
137-
attribute_actions=attribute_actions
138-
)
139-
return crypto_config, kwargs
140-
141-
def _table_crypto_config(self, **kwargs):
142-
"""Pull all encryption-specific parameters from the request and use them to build
143-
a crypto config for a single-table operation.
144-
145-
:returns: crypto config and updated kwargs
146-
:rtype: dynamodb_encryption_sdk.encrypted.CryptoConfig and dict
147-
"""
148-
return self._crypto_config(kwargs['TableName'], **kwargs)
149-
150-
def _batch_crypto_config(self, table_name):
151-
"""Build a crypto config for a specific table.
152-
153-
:param str table_name: Table for which to build crypto config
154-
:returns: crypto config
155-
:rtype: dynamodb_encryption_sdk.encrypted.CryptoConfig
156-
"""
157-
return self._crypto_config(table_name)[0]
158-
159128
def update_item(self, **kwargs):
160129
"""Update item is not yet supported."""
161130
raise NotImplementedError('"update_item" is not yet implemented')

src/dynamodb_encryption_sdk/encrypted/resource.py

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
from boto3.resources.base import ServiceResource
1818
from boto3.resources.collection import CollectionManager
1919

20-
from dynamodb_encryption_sdk.internal.utils import decrypt_batch_get_item, encrypt_batch_write_item, TableInfoCache
20+
from dynamodb_encryption_sdk.internal.utils import (
21+
crypto_config_from_cache, decrypt_batch_get_item, encrypt_batch_write_item, TableInfoCache
22+
)
2123
from dynamodb_encryption_sdk.material_providers import CryptographicMaterialsProvider
22-
from dynamodb_encryption_sdk.structures import AttributeActions, EncryptionContext
23-
from . import CryptoConfig
24+
from dynamodb_encryption_sdk.structures import AttributeActions
2425
from .item import decrypt_python_item, encrypt_python_item
2526
from .table import EncryptedTable
2627

@@ -137,6 +138,12 @@ def __attrs_post_init__(self):
137138
client=self._resource.meta.client,
138139
auto_refresh_table_indexes=self._auto_refresh_table_indexes
139140
)
141+
self._crypto_config = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
142+
crypto_config_from_cache,
143+
materials_provider=self._materials_provider,
144+
attribute_actions=self._attribute_actions,
145+
table_info_cache=self._table_info_cache
146+
)
140147
self.tables = EncryptedTablesCollectionManager( # attrs confuses pylint: disable=attribute-defined-outside-init
141148
collection=self._resource.tables,
142149
materials_provider=self._materials_provider,
@@ -166,24 +173,6 @@ def __getattr__(self, name):
166173
"""
167174
return getattr(self._resource, name)
168175

169-
def _crypto_config(self, table_name):
170-
"""Pull all encryption-specific parameters from the request and use them to build a crypto config.
171-
172-
:returns: crypto config
173-
:rtype: dynamodb_encryption_sdk.encrypted.CryptoConfig
174-
"""
175-
table_info = self._table_info_cache.table_info(table_name)
176-
177-
attribute_actions = self._attribute_actions.copy()
178-
attribute_actions.set_index_keys(*table_info.protected_index_keys())
179-
180-
crypto_config = CryptoConfig(
181-
materials_provider=self._materials_provider,
182-
encryption_context=EncryptionContext(**table_info.encryption_context_values),
183-
attribute_actions=attribute_actions
184-
)
185-
return crypto_config
186-
187176
def Table(self, name, **kwargs):
188177
# naming chosen to align with boto3 resource name, so pylint: disable=invalid-name
189178
"""Creates an EncryptedTable resource.

src/dynamodb_encryption_sdk/encrypted/table.py

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
import attr
1717
from boto3.resources.base import ServiceResource
1818

19-
from dynamodb_encryption_sdk.internal.utils import decrypt_get_item, decrypt_multi_get, encrypt_put_item
19+
from dynamodb_encryption_sdk.internal.utils import (
20+
crypto_config_from_kwargs, crypto_config_from_table_info,
21+
decrypt_get_item, decrypt_multi_get, encrypt_put_item
22+
)
2023
from dynamodb_encryption_sdk.material_providers import CryptographicMaterialsProvider
21-
from dynamodb_encryption_sdk.structures import AttributeActions, EncryptionContext, TableInfo
22-
from . import CryptoConfig
24+
from dynamodb_encryption_sdk.structures import AttributeActions, TableInfo
2325
from .item import decrypt_python_item, encrypt_python_item
2426

2527
__all__ = ('EncryptedTable',)
@@ -84,6 +86,15 @@ def __attrs_post_init__(self):
8486
self._attribute_actions = self._attribute_actions.copy()
8587
self._attribute_actions.set_index_keys(*self._table_info.protected_index_keys())
8688

89+
self._crypto_config = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
90+
crypto_config_from_kwargs,
91+
fallback=partial(
92+
crypto_config_from_table_info,
93+
materials_provider=self._materials_provider,
94+
attribute_actions=self._attribute_actions,
95+
table_info=self._table_info
96+
)
97+
)
8798
self.get_item = partial( # attrs confuses pylint: disable=attribute-defined-outside-init
8899
decrypt_get_item,
89100
decrypt_method=decrypt_python_item,
@@ -122,21 +133,3 @@ def __getattr__(self, name):
122133
def update_item(self, **kwargs):
123134
"""Update item is not yet supported."""
124135
raise NotImplementedError('"update_item" is not yet implemented')
125-
126-
def _crypto_config(self, **kwargs):
127-
"""Pull all encryption-specific parameters from the request and use them to build a crypto config.
128-
129-
:returns: crypto config and updated kwargs
130-
:rtype: dynamodb_encryption_sdk.encrypted.CryptoConfig and dict
131-
"""
132-
crypto_config = kwargs.pop('crypto_config', None)
133-
134-
if crypto_config is not None:
135-
return crypto_config, kwargs
136-
137-
crypto_config = CryptoConfig(
138-
materials_provider=self._materials_provider,
139-
encryption_context=EncryptionContext(**self._table_info.encryption_context_values),
140-
attribute_actions=self._attribute_actions
141-
)
142-
return crypto_config, kwargs

src/dynamodb_encryption_sdk/internal/utils.py

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
import attr
1515
import botocore.client
1616

17+
from dynamodb_encryption_sdk.encrypted import CryptoConfig
1718
from dynamodb_encryption_sdk.exceptions import InvalidArgumentError
1819
from dynamodb_encryption_sdk.internal.str_ops import to_bytes
19-
from dynamodb_encryption_sdk.structures import TableInfo
20+
from dynamodb_encryption_sdk.structures import EncryptionContext, TableInfo
2021

2122
try: # Python 3.5.0 and 3.5.1 have incompatible typing modules
2223
from typing import Callable, Dict, Text # noqa pylint: disable=unused-import
@@ -26,6 +27,7 @@
2627

2728
__all__ = (
2829
'sorted_key_map', 'TableInfoCache',
30+
'crypto_config_from_kwargs', 'crypto_config_from_table_info', 'crypto_config_from_cache',
2931
'decrypt_get_item', 'decrypt_multi_get', 'decrypt_batch_get_item',
3032
'encrypt_put_item', 'encrypt_batch_write_item'
3133
)
@@ -97,6 +99,50 @@ def validate_get_arguments(kwargs):
9799
raise InvalidArgumentError('Scan "Select" value of "{}" is not supported'.format(kwargs['Select']))
98100

99101

102+
def crypto_config_from_kwargs(fallback, **kwargs):
103+
"""Pull all encryption-specific parameters from the request and use them to build a crypto config.
104+
105+
:returns: crypto config and updated kwargs
106+
:rtype: dynamodb_encryption_sdk.encrypted.CryptoConfig and dict
107+
"""
108+
try:
109+
crypto_config = kwargs.pop('crypto_config')
110+
except KeyError:
111+
try:
112+
fallback_kwargs = {'table_name': kwargs['TableName']}
113+
except KeyError:
114+
fallback_kwargs = {}
115+
crypto_config = fallback(**fallback_kwargs)
116+
return crypto_config, kwargs
117+
118+
119+
def crypto_config_from_table_info(materials_provider, attribute_actions, table_info):
120+
"""Build a crypto config from the provided values and table info.
121+
122+
:returns: crypto config and updated kwargs
123+
:rtype: dynamodb_encryption_sdk.encrypted.CryptoConfig and dict
124+
"""
125+
return CryptoConfig(
126+
materials_provider=materials_provider,
127+
encryption_context=EncryptionContext(**table_info.encryption_context_values),
128+
attribute_actions=attribute_actions
129+
)
130+
131+
132+
def crypto_config_from_cache(materials_provider, attribute_actions, table_info_cache, table_name):
133+
"""Build a crypto config from the provided values, loading the table info from the provided cache.
134+
135+
:returns: crypto config and updated kwargs
136+
:rtype: dynamodb_encryption_sdk.encrypted.CryptoConfig and dict
137+
"""
138+
table_info = table_info_cache.table_info(table_name)
139+
140+
attribute_actions = attribute_actions.copy()
141+
attribute_actions.set_index_keys(*table_info.protected_index_keys())
142+
143+
return crypto_config_from_table_info(materials_provider, attribute_actions, table_info)
144+
145+
100146
def decrypt_multi_get(decrypt_method, crypto_config_method, read_method, **kwargs):
101147
# type: (Callable, Callable, Callable, **Any) -> Dict
102148
# TODO: narrow this down
@@ -156,7 +202,7 @@ def decrypt_batch_get_item(decrypt_method, crypto_config_method, read_method, **
156202
if request_crypto_config is not None:
157203
crypto_config = request_crypto_config
158204
else:
159-
crypto_config = crypto_config_method(table_name)
205+
crypto_config = crypto_config_method(table_name=table_name)
160206

161207
for pos, value in enumerate(items):
162208
items[pos] = decrypt_method(
@@ -198,7 +244,7 @@ def encrypt_batch_write_item(encrypt_method, crypto_config_method, write_method,
198244
if request_crypto_config is not None:
199245
crypto_config = request_crypto_config
200246
else:
201-
crypto_config = crypto_config_method(table_name)
247+
crypto_config = crypto_config_method(table_name=table_name)
202248

203249
for pos, value in enumerate(items):
204250
for request_type, item in value.items():

0 commit comments

Comments
 (0)