Skip to content

Commit

Permalink
track1-auth
Browse files Browse the repository at this point in the history
  • Loading branch information
jiasli committed Aug 1, 2024
1 parent d3d7dea commit 4d5bf3b
Show file tree
Hide file tree
Showing 5 changed files with 13 additions and 68 deletions.
11 changes: 3 additions & 8 deletions src/azure-cli-core/azure/cli/core/_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,18 +328,14 @@ def logout_all(self):
identity.logout_all_users()
identity.logout_all_service_principal()

def get_login_credentials(self, resource=None, client_id=None, subscription_id=None, aux_subscriptions=None,
aux_tenants=None):
def get_login_credentials(self, client_id=None, subscription_id=None, aux_subscriptions=None, aux_tenants=None):
"""Get a CredentialAdaptor instance to be used with both Track 1 and Track 2 SDKs.
:param resource: The resource ID to acquire an access token. Only provide it for Track 1 SDKs.
:param client_id:
:param subscription_id:
:param aux_subscriptions:
:param aux_tenants:
"""
resource = resource or self.cli_ctx.cloud.endpoints.active_directory_resource_id

if aux_tenants and aux_subscriptions:
raise CLIError("Please specify only one of aux_subscriptions and aux_tenants, not both")

Expand Down Expand Up @@ -368,11 +364,10 @@ def get_login_credentials(self, resource=None, client_id=None, subscription_id=N
for external_tenant in external_tenants:
external_credentials.append(self._create_credential(account, external_tenant, client_id=client_id))
from azure.cli.core.auth.credential_adaptor import CredentialAdaptor
cred = CredentialAdaptor(credential,
auxiliary_credentials=external_credentials,
resource=resource)
cred = CredentialAdaptor(credential, auxiliary_credentials=external_credentials)
else:
# managed identity
# TODO: Migrate MSIAuthentication to MSAL
cred = MsiAccountTypes.msi_auth_factory(managed_identity_type, managed_identity_id, resource)
return (cred,
str(account[_SUBSCRIPTION_ID]),
Expand Down
43 changes: 8 additions & 35 deletions src/azure-cli-core/azure/cli/core/auth/credential_adaptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,42 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import requests
from knack.log import get_logger
from knack.util import CLIError

from .util import resource_to_scopes, _normalize_scopes
from .util import _normalize_scopes

logger = get_logger(__name__)


class CredentialAdaptor:
def __init__(self, credential, resource=None, auxiliary_credentials=None):
def __init__(self, credential, auxiliary_credentials=None):
"""
Adaptor to both
- Track 1: msrest.authentication.Authentication, which exposes signed_session
- Track 2: azure.core.credentials.TokenCredential, which exposes get_token
Cross-tenant credential adaptor. It takes a main credential and auxiliary credentials.
It implements Track 2 SDK's azure.core.credentials.TokenCredential by exposing get_token.
:param credential: Main credential from .msal_authentication
:param resource: AAD resource for Track 1 only
:param auxiliary_credentials: Credentials from .msal_authentication for cross tenant authentication.
Details about cross tenant authentication:
https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/authenticate-multi-tenant
"""

self._credential = credential
self._auxiliary_credentials = auxiliary_credentials
self._resource = resource

def _get_token(self, scopes=None, **kwargs):
external_tenant_tokens = []
# If scopes is not provided, use CLI-managed resource
scopes = scopes or resource_to_scopes(self._resource)
try:
token = self._credential.get_token(*scopes, **kwargs)
if self._auxiliary_credentials:
external_tenant_tokens = [cred.get_token(*scopes) for cred in self._auxiliary_credentials]
return token, external_tenant_tokens
except requests.exceptions.SSLError as err:
from azure.cli.core.util import SSLERROR_TEMPLATE
raise CLIError(SSLERROR_TEMPLATE.format(str(err)))

def signed_session(self, session=None):
logger.debug("CredentialAdaptor.signed_session")
session = session or requests.Session()
token, external_tenant_tokens = self._get_token()
header = "{} {}".format('Bearer', token.token)
session.headers['Authorization'] = header
if external_tenant_tokens:
aux_tokens = ';'.join(['{} {}'.format('Bearer', tokens2.token) for tokens2 in external_tenant_tokens])
session.headers['x-ms-authorization-auxiliary'] = aux_tokens
return session

def get_token(self, *scopes, **kwargs):
"""Get an access token from the main credential."""
logger.debug("CredentialAdaptor.get_token: scopes=%r, kwargs=%r", scopes, kwargs)

# SDK azure-keyvault-keys 4.5.0b5 passes tenant_id as kwargs, but we don't support tenant_id for now,
# so discard it.
kwargs.pop('tenant_id', None)

scopes = _normalize_scopes(scopes)
token, _ = self._get_token(scopes, **kwargs)
return token
return self._credential.get_token(*scopes, **kwargs)

def get_auxiliary_tokens(self, *scopes, **kwargs):
"""Get access tokens from auxiliary credentials."""
# To test cross-tenant authentication, see https://github.com/Azure/azure-cli/issues/16691
if self._auxiliary_credentials:
return [cred.get_token(*scopes, **kwargs) for cred in self._auxiliary_credentials]
Expand Down

This file was deleted.

1 change: 1 addition & 0 deletions src/azure-cli-core/azure/cli/core/auth/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def scopes_to_resource(scopes):


def _normalize_scopes(scopes):
# TODO: Drop this function
"""Normalize scopes to workaround some SDK issues."""

# Track 2 SDKs generated before https://github.com/Azure/autorest.python/pull/239 don't maintain
Expand Down
8 changes: 1 addition & 7 deletions src/azure-cli-core/azure/cli/core/commands/client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,6 @@ def _get_mgmt_service_client(cli_ctx,
subscription_id=None,
api_version=None,
base_url_bound=True,
resource=None,
sdk_profile=None,
aux_subscriptions=None,
aux_tenants=None,
Expand All @@ -222,10 +221,6 @@ def _get_mgmt_service_client(cli_ctx,
from azure.cli.core._profile import Profile
logger.debug('Getting management service client client_type=%s', client_type.__name__)

# Track 1 SDK doesn't maintain the `resource`. The `resource` of the token is the one passed to
# get_login_credentials.
resource = resource or cli_ctx.cloud.endpoints.active_directory_resource_id

if credential:
# Use a custom credential
if not subscription_id:
Expand All @@ -234,8 +229,7 @@ def _get_mgmt_service_client(cli_ctx,
# Get a credential for the current `az login` context
profile = Profile(cli_ctx=cli_ctx)
credential, subscription_id, _ = profile.get_login_credentials(
subscription_id=subscription_id, resource=resource,
aux_subscriptions=aux_subscriptions, aux_tenants=aux_tenants)
subscription_id=subscription_id, aux_subscriptions=aux_subscriptions, aux_tenants=aux_tenants)

client_kwargs = {}
if base_url_bound:
Expand Down

0 comments on commit 4d5bf3b

Please sign in to comment.