diff --git a/src/azure-cli-core/azure/cli/core/_profile.py b/src/azure-cli-core/azure/cli/core/_profile.py index 79e08e5c91b..fef62704545 100644 --- a/src/azure-cli-core/azure/cli/core/_profile.py +++ b/src/azure-cli-core/azure/cli/core/_profile.py @@ -259,7 +259,7 @@ def _normalize_properties(self, user, subscriptions, is_service_principal, cert_ subscription_dict = { _SUBSCRIPTION_ID: s.id.rpartition('/')[2], _SUBSCRIPTION_NAME: display_name, - _STATE: s.state.value, + _STATE: s.state, _USER_ENTITY: { _USER_NAME: user, _USER_TYPE: _SERVICE_PRINCIPAL if is_service_principal else _USER @@ -815,12 +815,14 @@ def create_arm_client_factory(credentials): return arm_client_factory(credentials) from azure.cli.core.profiles._shared import get_client_class from azure.cli.core.profiles import ResourceType, get_api_version - from azure.cli.core.commands.client_factory import configure_common_settings + from azure.cli.core.commands.client_factory import _prepare_client_kwargs_track2 client_type = get_client_class(ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS) api_version = get_api_version(cli_ctx, ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS) + + client_kwargs = _prepare_client_kwargs_track2(cli_ctx) + # We don't need to change credential_scopes as 'scopes' is ignored by BasicTokenCredential anyway client = client_type(credentials, api_version=api_version, - base_url=self.cli_ctx.cloud.endpoints.resource_manager) - configure_common_settings(cli_ctx, client) + base_url=self.cli_ctx.cloud.endpoints.resource_manager, **client_kwargs) return client self._arm_client_factory = create_arm_client_factory @@ -838,9 +840,9 @@ def find_from_user_account(self, username, password, tenant, resource): self.user_id = token_entry[_TOKEN_ENTRY_USER_ID] if tenant is None: - result = self._find_using_common_tenant(token_entry[_ACCESS_TOKEN], resource) + result = self._find_using_common_tenant(token_entry, resource) else: - result = self._find_using_specific_tenant(tenant, token_entry[_ACCESS_TOKEN]) + result = self._find_using_specific_tenant(tenant, token_entry) return result def find_through_authorization_code_flow(self, tenant, resource, authority_url): @@ -857,9 +859,9 @@ def find_through_authorization_code_flow(self, tenant, resource, authority_url): self.user_id = token_entry[_TOKEN_ENTRY_USER_ID] logger.warning("You have logged in. Now let us find all the subscriptions to which you have access...") if tenant is None: - result = self._find_using_common_tenant(token_entry[_ACCESS_TOKEN], resource) + result = self._find_using_common_tenant(token_entry, resource) else: - result = self._find_using_specific_tenant(tenant, token_entry[_ACCESS_TOKEN]) + result = self._find_using_specific_tenant(tenant, token_entry) return result def find_through_interactive_flow(self, tenant, resource): @@ -869,16 +871,16 @@ def find_through_interactive_flow(self, tenant, resource): token_entry = context.acquire_token_with_device_code(resource, code, _CLIENT_ID) self.user_id = token_entry[_TOKEN_ENTRY_USER_ID] if tenant is None: - result = self._find_using_common_tenant(token_entry[_ACCESS_TOKEN], resource) + result = self._find_using_common_tenant(token_entry, resource) else: - result = self._find_using_specific_tenant(tenant, token_entry[_ACCESS_TOKEN]) + result = self._find_using_specific_tenant(tenant, token_entry) return result def find_from_service_principal_id(self, client_id, sp_auth, tenant, resource): context = self._create_auth_context(tenant, False) token_entry = sp_auth.acquire_token(context, resource, client_id) self.user_id = client_id - result = self._find_using_specific_tenant(tenant, token_entry[_ACCESS_TOKEN]) + result = self._find_using_specific_tenant(tenant, token_entry) self.tenants = [tenant] return result @@ -895,16 +897,17 @@ def _create_auth_context(self, tenant, use_token_cache=True): def _find_using_common_tenant(self, access_token, resource): import adal - from msrest.authentication import BasicTokenAuthentication + from azure.cli.core.adal_authentication import BasicTokenCredential all_subscriptions = [] empty_tenants = [] mfa_tenants = [] - token_credential = BasicTokenAuthentication({'access_token': access_token}) + token_credential = BasicTokenCredential(access_token) client = self._arm_client_factory(token_credential) tenants = client.tenants.list() for t in tenants: tenant_id = t.tenant_id + logger.debug("Finding subscriptions under tenant %s", tenant_id) # display_name is available since /tenants?api-version=2018-06-01, # not available in /tenants?api-version=2016-06-01 if not hasattr(t, 'display_name'): @@ -913,6 +916,7 @@ def _find_using_common_tenant(self, access_token, resource): t.display_name = t.additional_properties.get('displayName') temp_context = self._create_auth_context(tenant_id) try: + logger.debug("Acquiring a token with tenant=%s, resource=%s", tenant_id, resource) temp_credentials = temp_context.acquire_token(resource, self.user_id, _CLIENT_ID) except adal.AdalError as ex: # because user creds went through the 'common' tenant, the error here must be @@ -927,7 +931,7 @@ def _find_using_common_tenant(self, access_token, resource): continue subscriptions = self._find_using_specific_tenant( tenant_id, - temp_credentials[_ACCESS_TOKEN]) + temp_credentials) if not subscriptions: empty_tenants.append(t) @@ -969,9 +973,9 @@ def _find_using_common_tenant(self, access_token, resource): return all_subscriptions def _find_using_specific_tenant(self, tenant, access_token): - from msrest.authentication import BasicTokenAuthentication + from azure.cli.core.adal_authentication import BasicTokenCredential - token_credential = BasicTokenAuthentication({'access_token': access_token}) + token_credential = BasicTokenCredential(access_token) client = self._arm_client_factory(token_credential) subscriptions = client.subscriptions.list() all_subscriptions = [] diff --git a/src/azure-cli-core/azure/cli/core/adal_authentication.py b/src/azure-cli-core/azure/cli/core/adal_authentication.py index a4496b05d7a..cc6c4189c59 100644 --- a/src/azure-cli-core/azure/cli/core/adal_authentication.py +++ b/src/azure-cli-core/azure/cli/core/adal_authentication.py @@ -135,3 +135,13 @@ def set_token(self): def signed_session(self, session=None): logger.debug("MSIAuthenticationWrapper.signed_session invoked by Track 1 SDK") super().signed_session(session) + + +class BasicTokenCredential: + # pylint:disable=too-few-public-methods + """A Track 2 implementation of msrest.authentication.BasicTokenAuthentication.""" + def __init__(self, token_entry): + self.token_entry = token_entry + + def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument + return AccessToken(self.token_entry['accessToken'], int(self.token_entry['expiresIn'] + time.time())) diff --git a/src/azure-cli-core/azure/cli/core/commands/client_factory.py b/src/azure-cli-core/azure/cli/core/commands/client_factory.py index c8c4b5aa1bd..8361389c43a 100644 --- a/src/azure-cli-core/azure/cli/core/commands/client_factory.py +++ b/src/azure-cli-core/azure/cli/core/commands/client_factory.py @@ -110,12 +110,20 @@ def configure_common_settings(cli_ctx, client): client.config.generate_client_request_id = 'x-ms-client-request-id' not in cli_ctx.data['headers'] -def configure_common_settings_track2(cli_ctx): +def _prepare_client_kwargs_track2(cli_ctx): + """Prepare kwargs for Track 2 SDK client.""" client_kwargs = {} + # Change SSL verification behavior client_kwargs.update(_debug.change_ssl_cert_verification_track2()) + # Enable NetworkTraceLoggingPolicy which logs all headers (except Authorization) without being redacted client_kwargs['logging_enable'] = True + + # Disable ARMHttpLoggingPolicy which logs only allowed headers + from azure.core.pipeline.policies import SansIOHTTPPolicy + client_kwargs['http_logging_policy'] = SansIOHTTPPolicy() + client_kwargs['user_agent'] = get_az_user_agent() try: @@ -169,7 +177,7 @@ def _get_mgmt_service_client(cli_ctx, client_kwargs.update(kwargs) if is_track2(client_type): - client_kwargs.update(configure_common_settings_track2(cli_ctx)) + client_kwargs.update(_prepare_client_kwargs_track2(cli_ctx)) client_kwargs['credential_scopes'] = resource_to_scopes(resource) if subscription_bound: diff --git a/src/azure-cli-core/azure/cli/core/tests/test_profile.py b/src/azure-cli-core/azure/cli/core/tests/test_profile.py index b96af36a0ee..026ff80b910 100644 --- a/src/azure-cli-core/azure/cli/core/tests/test_profile.py +++ b/src/azure-cli-core/azure/cli/core/tests/test_profile.py @@ -314,7 +314,7 @@ def test_subscription_finder_constructor(self, get_api_mock): cli.cloud.endpoints.resource_manager = 'http://foo_arm' finder = SubscriptionFinder(cli, None, None, arm_client_factory=None) result = finder._arm_client_factory(mock.MagicMock()) - self.assertEqual(result.config.base_url, 'http://foo_arm') + self.assertEqual(result._client._base_url, 'http://foo_arm') @mock.patch('adal.AuthenticationContext', autospec=True) def test_get_auth_info_for_logged_in_service_principal(self, mock_auth_context): diff --git a/src/azure-cli-core/azure/cli/core/tests/test_profile_v2016_06_01.py b/src/azure-cli-core/azure/cli/core/tests/test_profile_v2016_06_01.py index 22d795da86f..46251983e14 100644 --- a/src/azure-cli-core/azure/cli/core/tests/test_profile_v2016_06_01.py +++ b/src/azure-cli-core/azure/cli/core/tests/test_profile_v2016_06_01.py @@ -294,7 +294,7 @@ def test_subscription_finder_constructor(self, get_api_mock): cli.cloud.endpoints.resource_manager = 'http://foo_arm' finder = SubscriptionFinder(cli, None, None, arm_client_factory=None) result = finder._arm_client_factory(mock.MagicMock()) - self.assertEqual(result.config.base_url, 'http://foo_arm') + self.assertEqual(result._client._base_url, 'http://foo_arm') @mock.patch('adal.AuthenticationContext', autospec=True) def test_get_auth_info_for_logged_in_service_principal(self, mock_auth_context): diff --git a/src/azure-cli-core/setup.py b/src/azure-cli-core/setup.py index 7912fb2e318..4ea33a96559 100644 --- a/src/azure-cli-core/setup.py +++ b/src/azure-cli-core/setup.py @@ -60,7 +60,7 @@ 'requests~=2.22', 'six~=1.12', 'pkginfo>=1.5.0.1', - 'azure-mgmt-resource==10.2.0', + 'azure-mgmt-resource==15.0.0', 'azure-mgmt-core==1.2.0' ] diff --git a/src/azure-cli/requirements.py3.Darwin.txt b/src/azure-cli/requirements.py3.Darwin.txt index e0a6b4a5be1..41bc8f6a093 100644 --- a/src/azure-cli/requirements.py3.Darwin.txt +++ b/src/azure-cli/requirements.py3.Darwin.txt @@ -68,7 +68,7 @@ azure-mgmt-redhatopenshift==0.1.0 azure-mgmt-redis==7.0.0rc1 azure-mgmt-relay==0.1.0 azure-mgmt-reservations==0.6.0 -azure-mgmt-resource==10.2.0 +azure-mgmt-resource==15.0.0 azure-mgmt-search==2.1.0 azure-mgmt-security==0.4.1 azure-mgmt-servicebus==0.6.0 diff --git a/src/azure-cli/requirements.py3.Linux.txt b/src/azure-cli/requirements.py3.Linux.txt index e0a6b4a5be1..41bc8f6a093 100644 --- a/src/azure-cli/requirements.py3.Linux.txt +++ b/src/azure-cli/requirements.py3.Linux.txt @@ -68,7 +68,7 @@ azure-mgmt-redhatopenshift==0.1.0 azure-mgmt-redis==7.0.0rc1 azure-mgmt-relay==0.1.0 azure-mgmt-reservations==0.6.0 -azure-mgmt-resource==10.2.0 +azure-mgmt-resource==15.0.0 azure-mgmt-search==2.1.0 azure-mgmt-security==0.4.1 azure-mgmt-servicebus==0.6.0 diff --git a/src/azure-cli/requirements.py3.windows.txt b/src/azure-cli/requirements.py3.windows.txt index eab211e1fd9..225db42598b 100644 --- a/src/azure-cli/requirements.py3.windows.txt +++ b/src/azure-cli/requirements.py3.windows.txt @@ -68,7 +68,7 @@ azure-mgmt-redhatopenshift==0.1.0 azure-mgmt-redis==7.0.0rc1 azure-mgmt-relay==0.1.0 azure-mgmt-reservations==0.6.0 -azure-mgmt-resource==10.2.0 +azure-mgmt-resource==15.0.0 azure-mgmt-search==2.1.0 azure-mgmt-security==0.4.1 azure-mgmt-servicebus==0.6.0