Skip to content

Commit bc8bfb2

Browse files
committed
feat: enable caching CMM to accept either MKP or keyring
1 parent 8a27aea commit bc8bfb2

File tree

3 files changed

+66
-51
lines changed

3 files changed

+66
-51
lines changed

src/aws_encryption_sdk/materials_managers/caching.py

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,20 @@
1616

1717
import attr
1818
import six
19+
from attr.validators import instance_of, optional
1920

20-
from ..caches import (
21+
from aws_encryption_sdk.caches import (
2122
CryptoMaterialsCacheEntryHints,
2223
build_decryption_materials_cache_key,
2324
build_encryption_materials_cache_key,
2425
)
25-
from ..caches.base import CryptoMaterialsCache
26-
from ..exceptions import CacheKeyError
27-
from ..internal.defaults import MAX_BYTES_PER_KEY, MAX_MESSAGES_PER_KEY
28-
from ..internal.str_ops import to_bytes
29-
from ..key_providers.base import MasterKeyProvider
26+
from aws_encryption_sdk.caches.base import CryptoMaterialsCache
27+
from aws_encryption_sdk.exceptions import CacheKeyError
28+
from aws_encryption_sdk.internal.defaults import MAX_BYTES_PER_KEY, MAX_MESSAGES_PER_KEY
29+
from aws_encryption_sdk.internal.str_ops import to_bytes
30+
from aws_encryption_sdk.key_providers.base import MasterKeyProvider
31+
from aws_encryption_sdk.keyrings.base import Keyring
32+
3033
from . import EncryptionMaterialsRequest
3134
from .base import CryptoMaterialsManager
3235
from .default import DefaultCryptoMaterialsManager
@@ -59,17 +62,17 @@ class CachingCryptoMaterialsManager(CryptoMaterialsManager):
5962
value. If no partition name is provided, a random UUID will be used.
6063
6164
.. note::
62-
Either `backing_materials_manager` or `master_key_provider` must be provided.
63-
`backing_materials_manager` will always be used if present.
64-
65-
:param cache: Crypto cache to use with material manager
66-
:type cache: aws_encryption_sdk.caches.base.CryptoMaterialsCache
67-
:param backing_materials_manager: Crypto material manager to back this caching material manager
68-
(either `backing_materials_manager` or `master_key_provider` required)
69-
:type backing_materials_manager: aws_encryption_sdk.materials_managers.base.CryptoMaterialsManager
70-
:param master_key_provider: Master key provider to use (either `backing_materials_manager` or
71-
`master_key_provider` required)
72-
:type master_key_provider: aws_encryption_sdk.key_providers.base.MasterKeyProvider
65+
Either ``backing_materials_manager``, ``keyring``, or ``master_key_provider`` must be provided.
66+
``backing_materials_manager`` will always be used if present.
67+
68+
:param CryptoMaterialsCache cache: Crypto cache to use with material manager
69+
:param CryptoMaterialsManager backing_materials_manager:
70+
Crypto material manager to back this caching material manager
71+
(either ``backing_materials_manager``, ``keyring``, or ``master_key_provider`` required)
72+
:param MasterKeyProvider master_key_provider: Master key provider to use
73+
(either ``backing_materials_manager``, ``keyring``, or ``master_key_provider`` required)
74+
:param Keyring keyring: Keyring to use
75+
(either ``backing_materials_manager``, ``keyring``, or ``master_key_provider`` required)
7376
:param float max_age: Maximum time in seconds that a cache entry may be kept in the cache
7477
:param int max_messages_encrypted: Maximum number of messages that may be encrypted under
7578
a cache entry (optional)
@@ -78,21 +81,14 @@ class CachingCryptoMaterialsManager(CryptoMaterialsManager):
7881
:param bytes partition_name: Partition name to use for this instance (optional)
7982
"""
8083

81-
cache = attr.ib(validator=attr.validators.instance_of(CryptoMaterialsCache))
82-
max_age = attr.ib(validator=attr.validators.instance_of(float))
83-
max_messages_encrypted = attr.ib(
84-
default=MAX_MESSAGES_PER_KEY, validator=attr.validators.instance_of(six.integer_types)
85-
)
86-
max_bytes_encrypted = attr.ib(default=MAX_BYTES_PER_KEY, validator=attr.validators.instance_of(six.integer_types))
87-
partition_name = attr.ib(
88-
default=None, converter=to_bytes, validator=attr.validators.optional(attr.validators.instance_of(bytes))
89-
)
90-
master_key_provider = attr.ib(
91-
default=None, validator=attr.validators.optional(attr.validators.instance_of(MasterKeyProvider))
92-
)
93-
backing_materials_manager = attr.ib(
94-
default=None, validator=attr.validators.optional(attr.validators.instance_of(CryptoMaterialsManager))
95-
)
84+
cache = attr.ib(validator=instance_of(CryptoMaterialsCache))
85+
max_age = attr.ib(validator=instance_of(float))
86+
max_messages_encrypted = attr.ib(default=MAX_MESSAGES_PER_KEY, validator=instance_of(six.integer_types))
87+
max_bytes_encrypted = attr.ib(default=MAX_BYTES_PER_KEY, validator=instance_of(six.integer_types))
88+
partition_name = attr.ib(default=None, converter=to_bytes, validator=optional(instance_of(bytes)))
89+
master_key_provider = attr.ib(default=None, validator=optional(instance_of(MasterKeyProvider)))
90+
backing_materials_manager = attr.ib(default=None, validator=optional(instance_of(CryptoMaterialsManager)))
91+
keyring = attr.ib(default=None, validator=optional(instance_of(Keyring)))
9692

9793
def __attrs_post_init__(self):
9894
"""Applies post-processing which cannot be handled by attrs."""
@@ -111,10 +107,21 @@ def __attrs_post_init__(self):
111107
if self.max_age <= 0.0:
112108
raise ValueError("max_age cannot be less than or equal to 0")
113109

110+
options_provided = [
111+
option is not None for option in (self.backing_materials_manager, self.keyring, self.master_key_provider)
112+
]
113+
provided_count = len([is_set for is_set in options_provided if is_set])
114+
115+
if provided_count != 1:
116+
raise TypeError("Exactly one of 'materials_manager', 'keyring', or 'key_provider' must be provided")
117+
114118
if self.backing_materials_manager is None:
115-
if self.master_key_provider is None:
116-
raise TypeError("Either backing_materials_manager or master_key_provider must be defined")
117-
self.backing_materials_manager = DefaultCryptoMaterialsManager(self.master_key_provider)
119+
if self.master_key_provider is not None:
120+
self.backing_materials_manager = DefaultCryptoMaterialsManager(
121+
master_key_provider=self.master_key_provider
122+
)
123+
else:
124+
self.backing_materials_manager = DefaultCryptoMaterialsManager(keyring=self.keyring)
118125

119126
if self.partition_name is None:
120127
self.partition_name = to_bytes(str(uuid.uuid4()))

src/aws_encryption_sdk/materials_managers/default.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ class DefaultCryptoMaterialsManager(CryptoMaterialsManager):
4040
The *keyring* parameter.
4141
4242
:param MasterKeyProvider master_key_provider: Master key provider to use
43-
(either `keyring` or `master_key_provider` is required)
43+
(either ``keyring`` or ``master_key_provider`` is required)
4444
:param Keyring keyring: Keyring to use
45-
(either `keyring` or `master_key_provider` is required)
45+
(either ``keyring`` or ``master_key_provider`` is required)
4646
"""
4747

4848
algorithm = ALGORITHM

test/unit/materials_managers/test_caching.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@
1313
"""Unit test suite for CachingCryptoMaterialsManager"""
1414
import pytest
1515
from mock import MagicMock, sentinel
16-
from pytest_mock import mocker # noqa pylint: disable=unused-import
1716

1817
import aws_encryption_sdk.materials_managers.caching
1918
from aws_encryption_sdk.caches.base import CryptoMaterialsCache
19+
from aws_encryption_sdk.caches.local import LocalCryptoMaterialsCache
2020
from aws_encryption_sdk.exceptions import CacheKeyError
2121
from aws_encryption_sdk.internal.defaults import MAX_BYTES_PER_KEY, MAX_MESSAGES_PER_KEY
2222
from aws_encryption_sdk.internal.str_ops import to_bytes
23-
from aws_encryption_sdk.key_providers.base import MasterKeyProvider
2423
from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager
2524
from aws_encryption_sdk.materials_managers.caching import CachingCryptoMaterialsManager
25+
from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager
26+
27+
from ..unit_test_utils import ephemeral_raw_aes_keyring, ephemeral_raw_aes_master_key
2628

2729
pytestmark = [pytest.mark.unit, pytest.mark.local]
2830

@@ -54,7 +56,7 @@ def fake_encryption_request():
5456
dict(max_messages_encrypted=None),
5557
dict(max_bytes_encrypted=None),
5658
dict(partition_name=55),
57-
dict(master_key_provider=None, backing_materials_manager=None),
59+
dict(master_key_provider=None, backing_materials_manager=None, keyring=None),
5860
),
5961
)
6062
def test_attrs_fail(invalid_kwargs):
@@ -88,20 +90,26 @@ def test_custom_partition_name(patch_uuid4):
8890
assert test.partition_name == custom_partition_name
8991

9092

91-
def test_mkp_to_default_cmm(mocker):
92-
mocker.patch.object(aws_encryption_sdk.materials_managers.caching, "DefaultCryptoMaterialsManager")
93-
mock_mkp = MagicMock(__class__=MasterKeyProvider)
93+
def test_mkp_to_default_cmm():
94+
mkp = ephemeral_raw_aes_master_key()
95+
9496
test = CachingCryptoMaterialsManager(
95-
cache=MagicMock(__class__=CryptoMaterialsCache), max_age=10.0, master_key_provider=mock_mkp
97+
cache=LocalCryptoMaterialsCache(capacity=10), max_age=10.0, master_key_provider=mkp
9698
)
9799

98-
aws_encryption_sdk.materials_managers.caching.DefaultCryptoMaterialsManager.assert_called_once_with(
99-
mock_mkp
100-
) # noqa pylint: disable=line-too-long
101-
assert (
102-
test.backing_materials_manager
103-
is aws_encryption_sdk.materials_managers.caching.DefaultCryptoMaterialsManager.return_value
104-
) # noqa pylint: disable=line-too-long
100+
assert isinstance(test.backing_materials_manager, DefaultCryptoMaterialsManager)
101+
assert test.backing_materials_manager.master_key_provider is mkp
102+
assert test.backing_materials_manager.keyring is None
103+
104+
105+
def test_keyring_to_default_cmm():
106+
keyring = ephemeral_raw_aes_keyring()
107+
108+
test = CachingCryptoMaterialsManager(cache=LocalCryptoMaterialsCache(capacity=10), max_age=10.0, keyring=keyring)
109+
110+
assert isinstance(test.backing_materials_manager, DefaultCryptoMaterialsManager)
111+
assert test.backing_materials_manager.keyring is keyring
112+
assert test.backing_materials_manager.master_key_provider is None
105113

106114

107115
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)