From bddbeeec248ed25ae514c222f012e778fbd4655c Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Tue, 18 Aug 2020 02:59:34 -0700 Subject: [PATCH] bump botocore (#823) --- .travis.yml | 8 +- CHANGES.rst | 3 + Pipfile | 2 + aiobotocore/__init__.py | 2 +- aiobotocore/credentials.py | 105 +++++++++- aiobotocore/endpoint.py | 5 + aiobotocore/signers.py | 2 + setup.py | 6 +- tests/botocore/test_credentials.py | 308 ++++++++++++++++++++++++++--- tests/test_patches.py | 13 +- 10 files changed, 418 insertions(+), 36 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4ce0a620..21635588 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,12 @@ language: python # Needed for Python 3.7 -dist: xenial +dist: bionic python: - - 3.6.10 - - 3.7.6 - - 3.8.1 + - 3.6.11 + - 3.7.8 + - 3.8.5 # moto is incompatible with 3.5 because it relies on the key order of its url path regex rules # - 3.5.9 diff --git a/CHANGES.rst b/CHANGES.rst index d20a0093..4557ef49 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,5 +1,8 @@ Changes ------- +1.1.0 (2020-08-18) +^^^^^^^^^^^^^^^^^^ +* bump botocore to 1.17.44 1.0.7 (2020-06-04) ^^^^^^^^^^^^^^^^^^ diff --git a/Pipfile b/Pipfile index 8266f60e..80ff0543 100644 --- a/Pipfile +++ b/Pipfile @@ -8,7 +8,9 @@ codecov = "*" coverage = "==5.0.3" flake8 = "==3.7.9" flake8-formatter-abspath = "==1.0.1" +docker = '<4' # fix: client version 1.39 is too new. Maximum supported API version is 1.38 moto = {extras = ["server"],version = "==1.3.14"} +idna = "==2.8" # broken pipenv resolver pytest = "==5.3.5" pytest-cov = "==2.8.1" pytest-asyncio = "==0.10.0" diff --git a/aiobotocore/__init__.py b/aiobotocore/__init__.py index 3fc2f534..b6f6aace 100644 --- a/aiobotocore/__init__.py +++ b/aiobotocore/__init__.py @@ -1,4 +1,4 @@ from .session import get_session, AioSession __all__ = ['get_session', 'AioSession'] -__version__ = '1.0.7' +__version__ = '1.1.0' diff --git a/aiobotocore/credentials.py b/aiobotocore/credentials.py index a1c530d6..db47af02 100644 --- a/aiobotocore/credentials.py +++ b/aiobotocore/credentials.py @@ -1,10 +1,16 @@ import asyncio +import datetime import logging import subprocess +import json from copy import deepcopy from typing import Optional +from hashlib import sha1 + +from dateutil.tz import tzutc from botocore import UNSIGNED +from botocore.config import Config import botocore.compat from botocore.credentials import EnvProvider, Credentials, RefreshableCredentials, \ ReadOnlyCredentials, ContainerProvider, ContainerMetadataFetcher, \ @@ -13,15 +19,19 @@ ProcessProvider, AssumeRoleWithWebIdentityProvider, _local_now, \ CachedCredentialFetcher, _serialize_if_needed, BaseAssumeRoleCredentialFetcher, \ AssumeRoleProvider, AssumeRoleCredentialFetcher, CredentialResolver, \ - CanonicalNameCredentialSourcer, BotoProvider, OriginalEC2Provider + CanonicalNameCredentialSourcer, BotoProvider, OriginalEC2Provider, \ + SSOProvider +from botocore.exceptions import UnauthorizedSSOTokenError from botocore.exceptions import MetadataRetrievalError, CredentialRetrievalError, \ InvalidConfigError, PartialCredentialsError, RefreshWithMFAUnsupportedError, \ UnknownCredentialError from botocore.compat import compat_shell_split +from botocore.utils import SSOTokenLoader from aiobotocore.utils import AioContainerMetadataFetcher, AioInstanceMetadataFetcher from aiobotocore.config import AioConfig + logger = logging.getLogger(__name__) @@ -132,6 +142,15 @@ def _create_web_identity_provider(self, profile_name, disable_env_vars): disable_env_vars=disable_env_vars, ) + def _create_sso_provider(self, profile_name): + return AioSSOProvider( + load_config=lambda: self._session.full_config, + client_creator=self._session.create_client, + profile_name=profile_name, + cache=self._cache, + token_cache=self._sso_token_cache, + ) + async def get_credentials(session): resolver = create_credential_resolver(session) @@ -795,3 +814,87 @@ async def load_credentials(self): # +1 # -js return None + + +class AioSSOCredentialFetcher(AioCachedCredentialFetcher): + def __init__(self, start_url, sso_region, role_name, account_id, + client_creator, token_loader=None, cache=None, + expiry_window_seconds=None): + self._client_creator = client_creator + self._sso_region = sso_region + self._role_name = role_name + self._account_id = account_id + self._start_url = start_url + self._token_loader = token_loader + + super(AioSSOCredentialFetcher, self).__init__( + cache, expiry_window_seconds + ) + + def _create_cache_key(self): + args = { + 'startUrl': self._start_url, + 'roleName': self._role_name, + 'accountId': self._account_id, + } + + args = json.dumps(args, sort_keys=True, separators=(',', ':')) + argument_hash = sha1(args.encode('utf-8')).hexdigest() + return self._make_file_safe(argument_hash) + + def _parse_timestamp(self, timestamp_ms): + # fromtimestamp expects seconds so: milliseconds / 1000 = seconds + timestamp_seconds = timestamp_ms / 1000.0 + timestamp = datetime.datetime.fromtimestamp(timestamp_seconds, tzutc()) + return _serialize_if_needed(timestamp) + + async def _get_credentials(self): + """Get credentials by calling SSO get role credentials.""" + config = Config( + signature_version=UNSIGNED, + region_name=self._sso_region, + ) + async with self._client_creator('sso', config=config) as client: + kwargs = { + 'roleName': self._role_name, + 'accountId': self._account_id, + 'accessToken': self._token_loader(self._start_url), + } + try: + response = await client.get_role_credentials(**kwargs) + except client.exceptions.UnauthorizedException: + raise UnauthorizedSSOTokenError() + credentials = response['roleCredentials'] + + credentials = { + 'ProviderType': 'sso', + 'Credentials': { + 'AccessKeyId': credentials['accessKeyId'], + 'SecretAccessKey': credentials['secretAccessKey'], + 'SessionToken': credentials['sessionToken'], + 'Expiration': self._parse_timestamp(credentials['expiration']), + } + } + return credentials + + +class AioSSOProvider(SSOProvider): + async def load(self): + sso_config = self._load_sso_config() + if not sso_config: + return None + + sso_fetcher = AioSSOCredentialFetcher( + sso_config['sso_start_url'], + sso_config['sso_region'], + sso_config['sso_role_name'], + sso_config['sso_account_id'], + self._client_creator, + token_loader=SSOTokenLoader(cache=self._token_cache), + cache=self.cache, + ) + + return AioDeferredRefreshableCredentials( + method=self.METHOD, + refresh_using=sso_fetcher.fetch_credentials, + ) diff --git a/aiobotocore/endpoint.py b/aiobotocore/endpoint.py index bfa4ae49..ca312ec0 100644 --- a/aiobotocore/endpoint.py +++ b/aiobotocore/endpoint.py @@ -178,6 +178,11 @@ async def _do_get_response(self, request, operation_model): parser = self._response_parser_factory.create_parser(protocol) parsed_response = parser.parse( response_dict, operation_model.output_shape) + if http_response.status_code >= 300: + self._add_modeled_error_fields( + response_dict, parsed_response, + operation_model, parser, + ) history_recorder.record('PARSED_RESPONSE', parsed_response) return (http_response, parsed_response), None diff --git a/aiobotocore/signers.py b/aiobotocore/signers.py index 0f49a475..99e97c8c 100644 --- a/aiobotocore/signers.py +++ b/aiobotocore/signers.py @@ -294,6 +294,8 @@ async def generate_presigned_post(self, Bucket, Key, Fields=None, Conditions=Non if fields is None: fields = {} + else: + fields = fields.copy() if conditions is None: conditions = [] diff --git a/setup.py b/setup.py index b0c5b5f5..f4b49300 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ # NOTE: When updating botocore make sure to update awscli/boto3 versions below install_requires = [ # pegged to also match items in `extras_require` - 'botocore>=1.15.32,<1.15.33', + 'botocore>=1.17.44,<1.17.45', 'aiohttp>=3.3.1', 'wrapt>=1.10.10', 'aioitertools>=0.5.1', @@ -19,8 +19,8 @@ def read(f): extras_require = { - 'awscli': ['awscli==1.18.32'], - 'boto3': ['boto3==1.12.32'], + 'awscli': ['awscli==1.18.121'], + 'boto3': ['boto3==1.14.44'], } diff --git a/tests/botocore/test_credentials.py b/tests/botocore/test_credentials.py index d41c773a..27caf81c 100644 --- a/tests/botocore/test_credentials.py +++ b/tests/botocore/test_credentials.py @@ -4,21 +4,26 @@ and adapted to work with asyncio and pytest """ import asyncio -import datetime +from datetime import datetime, timedelta import json import subprocess +from unittest import TestCase +from functools import partial import mock from typing import Optional import pytest import botocore.exceptions -from dateutil.tz import tzlocal +from botocore.stub import Stubber +from dateutil.tz import tzlocal, tzutc +from botocore.utils import datetime2timestamp from aiobotocore.session import AioSession from aiobotocore import credentials from botocore.configprovider import ConfigValueStore -from botocore.utils import FileWebIdentityTokenLoader +from botocore.utils import FileWebIdentityTokenLoader, SSOTokenLoader +from aiobotocore.credentials import AioSSOCredentialFetcher, AioSSOProvider # From class TestCredentials(BaseEnvVar): @@ -36,8 +41,8 @@ def test_credentials_normalization(access, secret): def refreshable_creds(): def _f(mock_time_return_value=None, refresher_return_value='METADATA'): refresher = mock.AsyncMock() - future_time = datetime.datetime.now(tzlocal()) + datetime.timedelta(hours=24) - expiry_time = datetime.datetime.now(tzlocal()) - datetime.timedelta(minutes=30) + future_time = datetime.now(tzlocal()) + timedelta(hours=24) + expiry_time = datetime.now(tzlocal()) - timedelta(minutes=30) metadata = { 'access_key': 'NEW-ACCESS', 'secret_key': 'NEW-SECRET', @@ -61,8 +66,8 @@ def _f(mock_time_return_value=None, refresher_return_value='METADATA'): @pytest.mark.asyncio async def test_refreshablecredentials_get_credentials_set(refreshable_creds): creds = refreshable_creds( - mock_time_return_value=(datetime.datetime.now(tzlocal()) - - datetime.timedelta(minutes=60)) + mock_time_return_value=(datetime.now(tzlocal()) - + timedelta(minutes=60)) ) assert not creds.refresh_needed() @@ -78,7 +83,7 @@ async def test_refreshablecredentials_get_credentials_set(refreshable_creds): @pytest.mark.asyncio async def test_refreshablecredentials_refresh_returns_empty_dict(refreshable_creds): creds = refreshable_creds( - mock_time_return_value=datetime.datetime.now(tzlocal()), + mock_time_return_value=datetime.now(tzlocal()), refresher_return_value={} ) @@ -92,7 +97,7 @@ async def test_refreshablecredentials_refresh_returns_empty_dict(refreshable_cre @pytest.mark.asyncio async def test_refreshablecredentials_refresh_returns_none(refreshable_creds): creds = refreshable_creds( - mock_time_return_value=datetime.datetime.now(tzlocal()), + mock_time_return_value=datetime.now(tzlocal()), refresher_return_value=None ) @@ -106,7 +111,7 @@ async def test_refreshablecredentials_refresh_returns_none(refreshable_creds): @pytest.mark.asyncio async def test_refreshablecredentials_refresh_returns_partial(refreshable_creds): creds = refreshable_creds( - mock_time_return_value=datetime.datetime.now(tzlocal()), + mock_time_return_value=datetime.now(tzlocal()), refresher_return_value={'access_key': 'akid'} ) @@ -121,7 +126,7 @@ async def test_refreshablecredentials_refresh_returns_partial(refreshable_creds) def deferrable_creds(): def _f(mock_time_return_value=None, refresher_return_value='METADATA'): refresher = mock.AsyncMock() - future_time = datetime.datetime.now(tzlocal()) + datetime.timedelta(hours=24) + future_time = datetime.now(tzlocal()) + timedelta(hours=24) metadata = { 'access_key': 'NEW-ACCESS', 'secret_key': 'NEW-SECRET', @@ -133,7 +138,7 @@ def _f(mock_time_return_value=None, refresher_return_value='METADATA'): else refresher_return_value mock_time = mock.Mock() mock_time.return_value = (mock_time_return_value or - datetime.datetime.now(tzlocal())) + datetime.now(tzlocal())) creds = credentials.AioDeferredRefreshableCredentials( refresher, 'iam-role', time_fetcher=mock_time ) @@ -192,13 +197,13 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): def some_future_time(): - timeobj = datetime.datetime.now(tzlocal()) - return timeobj + datetime.timedelta(hours=24) + timeobj = datetime.now(tzlocal()) + return timeobj + timedelta(hours=24) def get_expected_creds_from_response(response): expiration = response['Credentials']['Expiration'] - if isinstance(expiration, datetime.datetime): + if isinstance(expiration, datetime): expiration = expiration.isoformat() return { 'access_key': response['Credentials']['AccessKeyId'], @@ -281,7 +286,7 @@ async def test_assumerolefetcher_cache_in_cache_but_expired(): 'AccessKeyId': 'foo-cached', 'SecretAccessKey': 'bar-cached', 'SessionToken': 'baz-cached', - 'Expiration': datetime.datetime.now(tzlocal()), + 'Expiration': datetime.now(tzlocal()), } } } @@ -383,8 +388,8 @@ async def test_webidentfetcher_no_cache(): @pytest.mark.moto @pytest.mark.asyncio async def test_instancemetadata_load(): - timeobj = datetime.datetime.now(tzlocal()) - timestamp = (timeobj + datetime.timedelta(hours=24)).isoformat() + timeobj = datetime.now(tzlocal()) + timestamp = (timeobj + timedelta(hours=24)).isoformat() fetcher = mock.AsyncMock() fetcher.retrieve_iam_role_credentials.return_value = { @@ -642,7 +647,7 @@ async def test_assumerolecredprovider_mfa_cannot_refresh_credentials( assumerolecredprovider_config_loader(fake_config), client_creator, cache={}, profile_name='development', prompter=prompter) - local_now = mock.Mock(return_value=datetime.datetime.now(tzlocal())) + local_now = mock.Mock(return_value=datetime.now(tzlocal())) with mock.patch('aiobotocore.credentials._local_now', local_now): creds = await provider.load() await creds.get_frozen_credentials() @@ -656,7 +661,7 @@ async def test_assumerolecredprovider_mfa_cannot_refresh_credentials( @pytest.mark.moto @pytest.mark.asyncio async def test_assumerolewebidentprovider_no_cache(): - future = datetime.datetime.now(tzlocal()) + datetime.timedelta(hours=24) + future = datetime.now(tzlocal()) + timedelta(hours=24) response = { 'Credentials': { @@ -714,8 +719,8 @@ async def test_containerprovider_assume_role_no_cache(): fetcher = mock.AsyncMock() fetcher.full_url = full_url - timeobj = datetime.datetime.now(tzlocal()) - timestamp = (timeobj + datetime.timedelta(hours=24)).isoformat() + timeobj = datetime.now(tzlocal()) + timestamp = (timeobj + timedelta(hours=24)).isoformat() fetcher.retrieve_full_uri.return_value = { "AccessKeyId": "access_key", "SecretAccessKey": "secret_key", @@ -766,7 +771,7 @@ async def test_envvarprovider_env_var_absent(): @pytest.mark.moto @pytest.mark.asyncio async def test_envvarprovider_env_var_expiry(): - expiry_time = datetime.datetime.now(tzlocal()) - datetime.timedelta(hours=1) + expiry_time = datetime.now(tzlocal()) - timedelta(hours=1) environ = { 'AWS_ACCESS_KEY_ID': 'foo', 'AWS_SECRET_ACCESS_KEY': 'bar', @@ -1143,3 +1148,260 @@ async def test_session_credentials(): session = AioSession() creds = await session.get_credentials() assert creds == 'somecreds' + + +class Self: + pass + + +class _AsyncCtx: + def __init__(self, value): + self._value = value + + async def __aenter__(self): + return self._value + + async def __aexit__(self, exc_type, exc_val, exc_tb): + pass + + +# From class TestSSOCredentialFetcher: +@pytest.fixture +async def ssl_credential_fetcher_setup(): + async with AioSession().create_client('sso', region_name='us-east-1') as sso: + self = Self() + self.sso = sso + self.stubber = Stubber(self.sso) + self.mock_session = mock.Mock(spec=AioSession) + self.mock_session.create_client.return_value = _AsyncCtx(sso) + + self.cache = {} + self.sso_region = 'us-east-1' + self.start_url = 'https://d-92671207e4.awsapps.com/start' + self.role_name = 'test-role' + self.account_id = '1234567890' + self.access_token = 'some.sso.token' + # This is just an arbitrary point in time we can pin to + self.now = datetime(2008, 9, 23, 12, 26, 40, tzinfo=tzutc()) + # The SSO endpoint uses ms whereas the OIDC endpoint uses seconds + self.now_timestamp = 1222172800000 + + self.loader = mock.Mock(spec=SSOTokenLoader) + self.loader.return_value = self.access_token + self.fetcher = AioSSOCredentialFetcher( + self.start_url, self.sso_region, self.role_name, self.account_id, + self.mock_session.create_client, token_loader=self.loader, + cache=self.cache, + ) + + tc = TestCase() + self.assertEqual = tc.assertEqual + self.assertRaises = tc.assertRaises + yield self + + +@pytest.mark.moto +@pytest.mark.asyncio +async def test_sso_credential_fetcher_can_fetch_credentials( + ssl_credential_fetcher_setup): + self = ssl_credential_fetcher_setup + expected_params = { + 'roleName': self.role_name, + 'accountId': self.account_id, + 'accessToken': self.access_token, + } + expected_response = { + 'roleCredentials': { + 'accessKeyId': 'foo', + 'secretAccessKey': 'bar', + 'sessionToken': 'baz', + 'expiration': self.now_timestamp + 1000000, + } + } + self.stubber.add_response( + 'get_role_credentials', + expected_response, + expected_params=expected_params, + ) + with self.stubber: + credentials = await self.fetcher.fetch_credentials() + self.assertEqual(credentials['access_key'], 'foo') + self.assertEqual(credentials['secret_key'], 'bar') + self.assertEqual(credentials['token'], 'baz') + self.assertEqual(credentials['expiry_time'], '2008-09-23T12:43:20UTC') + cache_key = '048db75bbe50955c16af7aba6ff9c41a3131bb7e' + expected_cached_credentials = { + 'ProviderType': 'sso', + 'Credentials': { + 'AccessKeyId': 'foo', + 'SecretAccessKey': 'bar', + 'SessionToken': 'baz', + 'Expiration': '2008-09-23T12:43:20UTC', + } + } + self.assertEqual(self.cache[cache_key], expected_cached_credentials) + + +@pytest.mark.moto +@pytest.mark.asyncio +async def test_sso_cred_fetcher_raises_helpful_message_on_unauthorized_exception( + ssl_credential_fetcher_setup): + self = ssl_credential_fetcher_setup + expected_params = { + 'roleName': self.role_name, + 'accountId': self.account_id, + 'accessToken': self.access_token, + } + self.stubber.add_client_error( + 'get_role_credentials', + service_error_code='UnauthorizedException', + expected_params=expected_params, + ) + with self.assertRaises(botocore.exceptions.UnauthorizedSSOTokenError): + with self.stubber: + await self.fetcher.fetch_credentials() + + +# from TestSSOProvider +@pytest.fixture +async def sso_provider_setup(): + self = Self() + async with AioSession().create_client('sso', region_name='us-east-1') as sso: + self.sso = sso + self.stubber = Stubber(self.sso) + self.mock_session = mock.Mock(spec=AioSession) + self.mock_session.create_client.return_value = _AsyncCtx(sso) + + self.sso_region = 'us-east-1' + self.start_url = 'https://d-92671207e4.awsapps.com/start' + self.role_name = 'test-role' + self.account_id = '1234567890' + self.access_token = 'some.sso.token' + + self.profile_name = 'sso-profile' + self.config = { + 'sso_region': self.sso_region, + 'sso_start_url': self.start_url, + 'sso_role_name': self.role_name, + 'sso_account_id': self.account_id, + } + self.expires_at = datetime.now(tzlocal()) + timedelta(hours=24) + self.cached_creds_key = '048db75bbe50955c16af7aba6ff9c41a3131bb7e' + self.cached_token_key = '13f9d35043871d073ab260e020f0ffde092cb14b' + self.cache = { + self.cached_token_key: { + 'accessToken': self.access_token, + 'expiresAt': self.expires_at.strftime('%Y-%m-%dT%H:%M:%S%Z'), + } + } + self._mock_load_config = partial(_mock_load_config, self) + self._add_get_role_credentials_response = partial( + _add_get_role_credentials_response, self) + self.provider = AioSSOProvider( + load_config=self._mock_load_config, + client_creator=self.mock_session.create_client, + profile_name=self.profile_name, + cache=self.cache, + token_cache=self.cache, + ) + + self.expected_get_role_credentials_params = { + 'roleName': self.role_name, + 'accountId': self.account_id, + 'accessToken': self.access_token, + } + expiration = datetime2timestamp(self.expires_at) + self.expected_get_role_credentials_response = { + 'roleCredentials': { + 'accessKeyId': 'foo', + 'secretAccessKey': 'bar', + 'sessionToken': 'baz', + 'expiration': int(expiration * 1000), + } + } + + tc = TestCase() + self.assertEqual = tc.assertEqual + self.assertRaises = tc.assertRaises + + yield self + + +def _mock_load_config(self): + return { + 'profiles': { + self.profile_name: self.config, + } + } + + +def _add_get_role_credentials_response(self): + self.stubber.add_response( + 'get_role_credentials', + self.expected_get_role_credentials_response, + self.expected_get_role_credentials_params, + ) + + +def test_load_sso_credentials_without_cache(self): + self._add_get_role_credentials_response() + with self.stubber: + credentials = self.provider.load() + self.assertEqual(credentials.access_key, 'foo') + self.assertEqual(credentials.secret_key, 'bar') + self.assertEqual(credentials.token, 'baz') + + +@pytest.mark.moto +@pytest.mark.asyncio +async def test_load_sso_credentials_with_cache(sso_provider_setup): + self = sso_provider_setup + + cached_creds = { + 'Credentials': { + 'AccessKeyId': 'cached-akid', + 'SecretAccessKey': 'cached-sak', + 'SessionToken': 'cached-st', + 'Expiration': self.expires_at.strftime('%Y-%m-%dT%H:%M:%S%Z'), + } + } + self.cache[self.cached_creds_key] = cached_creds + credentials = await self.provider.load() + credentials = await credentials.get_frozen_credentials() + self.assertEqual(credentials.access_key, 'cached-akid') + self.assertEqual(credentials.secret_key, 'cached-sak') + self.assertEqual(credentials.token, 'cached-st') + + +@pytest.mark.moto +@pytest.mark.asyncio +async def test_load_sso_credentials_with_cache_expired(sso_provider_setup): + self = sso_provider_setup + cached_creds = { + 'Credentials': { + 'AccessKeyId': 'expired-akid', + 'SecretAccessKey': 'expired-sak', + 'SessionToken': 'expired-st', + 'Expiration': '2002-10-22T20:52:11UTC', + } + } + self.cache[self.cached_creds_key] = cached_creds + + self._add_get_role_credentials_response() + with self.stubber: + credentials = await self.provider.load() + credentials = await credentials.get_frozen_credentials() + + self.assertEqual(credentials.access_key, 'foo') + self.assertEqual(credentials.secret_key, 'bar') + self.assertEqual(credentials.token, 'baz') + + +@pytest.mark.moto +@pytest.mark.asyncio +async def test_required_config_not_set(sso_provider_setup): + self = sso_provider_setup + del self.config['sso_start_url'] + # If any required configuration is missing we should get an error + with self.assertRaises(botocore.exceptions.InvalidConfigError): + await self.provider.load() diff --git a/tests/test_patches.py b/tests/test_patches.py index 718886b8..707f4bab 100644 --- a/tests/test_patches.py +++ b/tests/test_patches.py @@ -36,7 +36,7 @@ AssumeRoleWithWebIdentityProvider, AssumeRoleProvider, \ CanonicalNameCredentialSourcer, BotoProvider, OriginalEC2Provider, \ create_credential_resolver, get_credentials, create_mfa_serial_refresher, \ - AssumeRoleWithWebIdentityCredentialFetcher + AssumeRoleWithWebIdentityCredentialFetcher, SSOCredentialFetcher, SSOProvider # This file ensures that our private patches will work going forward. If a # method gets updated this will assert and someone will need to validate: @@ -114,7 +114,10 @@ {'432409f81601dbeea9ec187d433d190ab7c5ab2f'}, RefreshableCredentials.get_frozen_credentials: {'f661c84a8b759786e011f0b1e8a468a0c6294e36'}, - + SSOCredentialFetcher: + {'e092b115155a06760af6f3c72ccef120979b1201'}, + SSOProvider.load: + {'f43d79e1520b2a7b7ef85cd537f41e19d4bce806'}, CachedCredentialFetcher._get_credentials: {'02a7d13599d972e3f258d2b53f87eeda4cc3e3a4'}, CachedCredentialFetcher.fetch_credentials: @@ -150,6 +153,8 @@ {'f9a40d4211f6e663ba2ae9682fba5306152178c5'}, ProfileProviderBuilder._create_web_identity_provider: {'0907c1ad5573bc5c0fc87efb601a6c4c3fcf34ae'}, + ProfileProviderBuilder._create_sso_provider: + {'258e6d07bdf40ea2c7551bae0cd6e1ab58e4e502'}, ConfigProvider.load: {'8fb32140086dce65fa28be8edd3ac0d22698c3ae'}, SharedCredentialProvider.load: {'c0be1fe376d25952461ca18d9bef4b4340203441'}, ProcessProvider.__init__: {'2e870ec0c6b0bc8483fa9b1159ef68bbd7a12c56'}, @@ -188,7 +193,7 @@ Endpoint.create_request: {'4ccc14de2fd52f5c60017e55ff8e5b78bbaabcec'}, Endpoint._send_request: {'50ab33d6f16e75594d01ab1c2ec6b7c7903798db'}, Endpoint._get_response: {'46c3a8cb4ff7672b75193ce5571dbea48aa9da75'}, - Endpoint._do_get_response: {'df29f099d26dc057834c7b25d3b5217f1f7acbe4'}, + Endpoint._do_get_response: {'0bc57fbacf3c49ec5cd243b014d531a38b9b4138'}, Endpoint._needs_retry: {'0f40f52d8c90c6e10b4c9e1c4a5ca00ef2c72850'}, Endpoint._send: {'644c7e5bb88fecaa0b2a204411f8c7e69cc90bf1'}, @@ -246,7 +251,7 @@ S3PostPresigner.generate_presigned_post: {'b91d50bae4122d7ab540653865ec9294520ac0e1'}, add_generate_presigned_post: {'e30360f2bd893fabf47f5cdb04b0de420ccd414d'}, - generate_presigned_post: {'85e9ebe0412cb10716bf84a1533798882f3fc79f'}, + generate_presigned_post: {'e9756488cf1ceb68d23b36688f3d0767505f3c77'}, add_generate_db_auth_token: {'f61014e6fac4b5c7ee7ac2d2bec15fb16fa9fbe5'}, generate_db_auth_token: {'5f5a758458c007107a23124192339f747472dc75'},