Skip to content

Commit 7ca3bfa

Browse files
authored
Merge pull request #47 from mattsb42-aws/user-agent
Partial fix (and actually test for) user agent modification
2 parents cdf85a7 + 6ad0ec0 commit 7ca3bfa

File tree

7 files changed

+68
-54
lines changed

7 files changed

+68
-54
lines changed

src/aws_encryption_sdk/identifiers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from aws_encryption_sdk.exceptions import InvalidAlgorithmError
2323

2424
__version__ = '1.3.3'
25-
USER_AGENT_SUFFIX = 'AwsEncryptionSdkPython-KMSMasterKey/{}'.format(__version__)
25+
USER_AGENT_SUFFIX = 'AwsEncryptionSdkPython/{}'.format(__version__)
2626

2727

2828
class EncryptionSuite(Enum):

src/aws_encryption_sdk/internal/utils/__init__.py

-16
Original file line numberDiff line numberDiff line change
@@ -165,19 +165,3 @@ def source_data_key_length_check(source_data_key, algorithm):
165165
actual=len(source_data_key.data_key),
166166
required=algorithm.kdf_input_len
167167
))
168-
169-
170-
def extend_user_agent_suffix(user_agent, suffix):
171-
"""Adds a suffix to the provided user agent.
172-
173-
:param str user_agent: Existing user agent (None == not yet defined)
174-
:param str suffix: Desired suffix to add to user agent
175-
:returns: User agent with suffix
176-
:rtype: str
177-
"""
178-
if user_agent is None:
179-
user_agent = ''
180-
else:
181-
user_agent += ' '
182-
user_agent += suffix
183-
return user_agent

src/aws_encryption_sdk/key_providers/kms.py

+41-17
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
import attr
1717
import boto3
1818
import botocore.client
19+
import botocore.config
1920
from botocore.exceptions import ClientError
2021
import botocore.session
2122

2223
from aws_encryption_sdk.exceptions import DecryptKeyError, EncryptKeyError, GenerateKeyError, UnknownRegionError
2324
from aws_encryption_sdk.identifiers import USER_AGENT_SUFFIX
2425
from aws_encryption_sdk.internal.str_ops import to_str
25-
from aws_encryption_sdk.internal.utils import extend_user_agent_suffix
2626
from aws_encryption_sdk.key_providers.base import (
2727
MasterKey, MasterKeyConfig, MasterKeyProvider, MasterKeyProviderConfig
2828
)
@@ -33,6 +33,28 @@
3333
_PROVIDER_ID = 'aws-kms'
3434

3535

36+
def _region_from_key_id(key_id, default_region=None):
37+
"""Determine the target region from a key ID, falling back to a default region if provided.
38+
39+
:param str key_id: AWS KMS key ID
40+
:param str default_region: Region to use if no region found in key_id
41+
:returns: region name
42+
:rtype: str
43+
:raises UnknownRegionError: if no region found in key_id and no default_region provided
44+
"""
45+
try:
46+
region_name = key_id.split(':', 4)[3]
47+
if default_region is None:
48+
default_region = region_name
49+
except IndexError:
50+
if default_region is None:
51+
raise UnknownRegionError(
52+
'No default region found and no region determinable from key id: {}'.format(key_id)
53+
)
54+
region_name = default_region
55+
return region_name
56+
57+
3658
@attr.s(hash=True)
3759
class KMSMasterKeyProviderConfig(MasterKeyProviderConfig):
3860
"""Configuration object for KMSMasterKeyProvider objects.
@@ -101,6 +123,7 @@ def __init__(self, **kwargs): # pylint: disable=unused-argument
101123

102124
def _process_config(self):
103125
"""Traverses the config and adds master keys and regional clients as needed."""
126+
self._user_agent_adding_config = botocore.config.Config(user_agent_extra=USER_AGENT_SUFFIX)
104127
if self.config.key_ids:
105128
self.add_master_keys_from_list(self.config.key_ids)
106129
if self.config.region_names:
@@ -120,7 +143,7 @@ def add_regional_client(self, region_name):
120143
self._regional_clients[region_name] = boto3.session.Session(
121144
region_name=region_name,
122145
botocore_session=self.config.botocore_session
123-
).client('kms')
146+
).client('kms', config=self._user_agent_adding_config)
124147

125148
def add_regional_clients_from_list(self, region_names):
126149
"""Adds multiple regional clients for the specified regions if they do not already exist.
@@ -135,16 +158,7 @@ def _client(self, key_id):
135158
136159
:param str key_id: KMS CMK ID
137160
"""
138-
try:
139-
region_name = key_id.split(':', 4)[3]
140-
if self.default_region is None:
141-
self.default_region = region_name
142-
except IndexError:
143-
if self.default_region is None:
144-
raise UnknownRegionError(
145-
'No default region found and no region determinable from key id: {}'.format(key_id)
146-
)
147-
region_name = self.default_region
161+
region_name = _region_from_key_id(key_id, self.default_region)
148162
self.add_regional_client(region_name)
149163
return self._regional_clients[region_name]
150164

@@ -174,14 +188,28 @@ class KMSMasterKeyConfig(MasterKeyConfig):
174188
"""
175189

176190
provider_id = _PROVIDER_ID
177-
client = attr.ib(hash=True, validator=attr.validators.instance_of(botocore.client.BaseClient))
191+
client = attr.ib(
192+
hash=True,
193+
validator=attr.validators.instance_of(botocore.client.BaseClient)
194+
)
178195
grant_tokens = attr.ib(
179196
hash=True,
180197
default=attr.Factory(tuple),
181198
validator=attr.validators.instance_of(tuple),
182199
converter=tuple
183200
)
184201

202+
@client.default
203+
def client_default(self):
204+
"""Create a client if one was not provided."""
205+
try:
206+
region_name = _region_from_key_id(to_str(self.key_id))
207+
kwargs = dict(region_name=region_name)
208+
except UnknownRegionError:
209+
kwargs = {}
210+
botocore_config = botocore.config.Config(user_agent_extra=USER_AGENT_SUFFIX)
211+
return boto3.session.Session(**kwargs).client('kms', config=botocore_config)
212+
185213

186214
class KMSMasterKey(MasterKey):
187215
"""Master Key class for KMS CMKs.
@@ -200,10 +228,6 @@ class KMSMasterKey(MasterKey):
200228
def __init__(self, **kwargs): # pylint: disable=unused-argument
201229
"""Performs transformations needed for KMS."""
202230
self._key_id = to_str(self.key_id) # KMS client requires str, not bytes
203-
self.config.client.meta.config.user_agent_extra = extend_user_agent_suffix(
204-
user_agent=self.config.client.meta.config.user_agent_extra,
205-
suffix=USER_AGENT_SUFFIX
206-
)
207231

208232
def _generate_data_key(self, algorithm, encryption_context=None):
209233
"""Generates data key and returns plaintext and ciphertext of key.

test/integration/test_i_aws_encrytion_sdk_client.py

+23-2
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
# language governing permissions and limitations under the License.
1313
"""Integration test suite for `aws_encryption_sdk`."""
1414
import io
15+
import logging
1516
import unittest
1617

1718
import pytest
1819

1920
import aws_encryption_sdk
20-
from aws_encryption_sdk.identifiers import Algorithm
21-
from .integration_test_utils import setup_kms_master_key_provider
21+
from aws_encryption_sdk.identifiers import Algorithm, USER_AGENT_SUFFIX
22+
from aws_encryption_sdk.key_providers.kms import KMSMasterKey
23+
from .integration_test_utils import get_cmk_arn, setup_kms_master_key_provider
2224

2325
pytestmark = [pytest.mark.integ]
2426

@@ -40,6 +42,25 @@
4042
}
4143

4244

45+
def test_encrypt_verify_user_agent_kms_master_key_provider(caplog):
46+
caplog.set_level(level=logging.DEBUG)
47+
mkp = setup_kms_master_key_provider()
48+
mk = mkp.master_key(get_cmk_arn())
49+
50+
mk.generate_data_key(algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, encryption_context={})
51+
52+
assert USER_AGENT_SUFFIX in caplog.text
53+
54+
55+
def test_encrypt_verify_user_agent_kms_master_key(caplog):
56+
caplog.set_level(level=logging.DEBUG)
57+
mk = KMSMasterKey(key_id=get_cmk_arn())
58+
59+
mk.generate_data_key(algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, encryption_context={})
60+
61+
assert USER_AGENT_SUFFIX in caplog.text
62+
63+
4364
class TestKMSThickClientIntegration(unittest.TestCase):
4465

4566
def setUp(self):

test/unit/test_providers_kms_master_key.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import six
2121

2222
from aws_encryption_sdk.exceptions import DecryptKeyError, EncryptKeyError, GenerateKeyError
23-
from aws_encryption_sdk.identifiers import Algorithm, USER_AGENT_SUFFIX
23+
from aws_encryption_sdk.identifiers import Algorithm
2424
from aws_encryption_sdk.key_providers.base import MasterKey
2525
from aws_encryption_sdk.key_providers.kms import KMSMasterKey, KMSMasterKeyConfig
2626
from aws_encryption_sdk.structures import DataKey, EncryptedDataKey, MasterKeyInfo
@@ -96,16 +96,10 @@ def test_config_grant_tokens(self):
9696
)
9797
assert test.grant_tokens is self.mock_grant_tokens
9898

99-
@patch('aws_encryption_sdk.key_providers.kms.extend_user_agent_suffix')
100-
def test_init(self, patch_extend_user_agent_suffix):
99+
def test_init(self):
101100
self.mock_client.meta.config.user_agent_extra = sentinel.user_agent_extra
102101
test = KMSMasterKey(config=self.mock_kms_mkc_1)
103102
assert test._key_id == VALUES['arn'].decode('utf-8')
104-
patch_extend_user_agent_suffix.assert_called_once_with(
105-
user_agent=sentinel.user_agent_extra,
106-
suffix=USER_AGENT_SUFFIX
107-
)
108-
assert self.mock_client.meta.config.user_agent_extra == patch_extend_user_agent_suffix.return_value
109103

110104
def test_generate_data_key(self):
111105
test = KMSMasterKey(config=self.mock_kms_mkc_3)

test/unit/test_providers_kms_master_key_provider.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def test_add_regional_client_new(self):
9999
region_name='ex_region_name',
100100
botocore_session=ANY
101101
)
102-
self.mock_boto3_session_instance.client.assert_called_once_with('kms')
102+
self.mock_boto3_session_instance.client.assert_called_once_with('kms', config=test._user_agent_adding_config)
103103
assert test._regional_clients['ex_region_name'] is self.mock_boto3_client_instance
104104

105105
def test_add_regional_client_exists(self):

test/unit/test_utils.py

-9
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,6 @@
2727
pytestmark = [pytest.mark.unit, pytest.mark.local]
2828

2929

30-
@pytest.mark.parametrize('user_agent, suffix, output', (
31-
(None, 'test_suffix', 'test_suffix'),
32-
('test_existing_suffix', 'test_suffix', 'test_existing_suffix test_suffix')
33-
))
34-
def test_extend_user_agent_suffix(user_agent, suffix, output):
35-
test = aws_encryption_sdk.internal.utils.extend_user_agent_suffix(user_agent, suffix)
36-
assert test == output
37-
38-
3930
class TestUtils(unittest.TestCase):
4031

4132
def setUp(self):

0 commit comments

Comments
 (0)