Skip to content

Commit 3e46e6d

Browse files
yugangw-msftderekbekoe
authored andcommitted
cloud shell login: bring back the raw token support as the shell is not ready to switch (#5772)
1 parent 92671a7 commit 3e46e6d

2 files changed

Lines changed: 79 additions & 15 deletions

File tree

  • src
    • azure-cli-core/azure/cli/core
    • command_modules/azure-cli-profile/azure/cli/command_modules/profile

src/azure-cli-core/azure/cli/core/_profile.py

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,52 @@ def find_subscriptions_on_login(self,
189189
# use deepcopy as we don't want to persist these changes to file.
190190
return deepcopy(consolidated)
191191

192+
def find_subscriptions_in_cloud_console_thru_raw_token(self, tokens):
193+
from datetime import datetime, timedelta
194+
import jwt
195+
arm_token = tokens[0] # cloud shell gurantees that the 1st is for ARM
196+
arm_token_decoded = jwt.decode(arm_token, verify=False, algorithms=['RS256'])
197+
tenant = arm_token_decoded['tid']
198+
user_id = arm_token_decoded['unique_name'].split('#')[-1]
199+
subscription_finder = SubscriptionFinder(self.cli_ctx, self.auth_ctx_factory, None)
200+
subscriptions = subscription_finder.find_from_raw_token(tenant, arm_token)
201+
consolidated = self._normalize_properties(user_id, subscriptions, is_service_principal=False)
202+
self._set_subscriptions(consolidated)
203+
204+
# construct token entries to cache
205+
decoded_tokens = [arm_token_decoded]
206+
for t in tokens[1:]:
207+
decoded_tokens.append(jwt.decode(t, verify=False, algorithms=['RS256']))
208+
final_tokens = []
209+
# Note, setting expiration time at 2700 seconds is bit arbitrary, but should not matter
210+
# as shell should update us with new ones every 10~15 minutes
211+
for t in decoded_tokens:
212+
final_tokens.append({
213+
'_clientId': _CLIENT_ID,
214+
'expiresIn': '2700',
215+
'expiresOn': str(datetime.now() + timedelta(seconds=2700)),
216+
'userId': t['unique_name'].split('#')[-1],
217+
'_authority': self.cli_ctx.cloud.endpoints.active_directory.rstrip('/') + '/' + t['tid'],
218+
'resource': t['aud'],
219+
'isMRRT': True,
220+
'accessToken': tokens[decoded_tokens.index(t)],
221+
'tokenType': 'Bearer',
222+
})
223+
224+
# merging with existing cached ones
225+
for t in final_tokens:
226+
cached_tokens = [entry for _, entry in self._creds_cache.adal_token_cache.read_items()]
227+
to_delete = [c for c in cached_tokens if (c['_clientId'].lower() == t['_clientId'].lower() and
228+
c['resource'].lower() == t['resource'].lower() and
229+
c['_authority'].lower() == t['_authority'].lower() and
230+
c['userId'].lower() == t['userId'].lower())]
231+
if to_delete:
232+
self._creds_cache.adal_token_cache.remove(to_delete)
233+
self._creds_cache.adal_token_cache.add(final_tokens)
234+
self._creds_cache.persist_cached_creds()
235+
236+
return deepcopy(consolidated)
237+
192238
def _normalize_properties(self, user, subscriptions, is_service_principal):
193239
consolidated = []
194240
for s in subscriptions:
@@ -410,8 +456,8 @@ def _retrieve_token():
410456
identity_id, msi_port = Profile._try_parse_for_msi_port(account[_SUBSCRIPTION_NAME])
411457
if msi_port is not None:
412458
return Profile.get_msi_token(resource, msi_port, identity_id)
413-
elif in_cloud_console() and account[_USER_ENTITY].get(_CLOUD_SHELL_ID):
414-
return Profile.get_msi_token(resource, _get_cloud_console_token_endpoint())
459+
# elif in_cloud_console() and account[_USER_ENTITY].get(_CLOUD_SHELL_ID):
460+
# return Profile.get_msi_token(resource, _get_cloud_console_token_endpoint())
415461
elif user_type == _USER:
416462
return self._creds_cache.retrieve_token_for_user(username_or_sp_id,
417463
account[_TENANT_ID], resource)
@@ -448,8 +494,8 @@ def get_raw_token(self, resource=None, subscription=None):
448494
identity_id, msi_port = Profile._try_parse_for_msi_port(account[_SUBSCRIPTION_NAME])
449495
if msi_port is not None:
450496
creds = Profile.get_msi_token(resource, msi_port, identity_id)
451-
elif in_cloud_console() and account[_USER_ENTITY].get(_CLOUD_SHELL_ID):
452-
creds = Profile.get_msi_token(resource, _get_cloud_console_token_endpoint())
497+
# elif in_cloud_console() and account[_USER_ENTITY].get(_CLOUD_SHELL_ID):
498+
# creds = Profile.get_msi_token(resource, _get_cloud_console_token_endpoint())
453499
elif user_type == _USER:
454500
creds = self._creds_cache.retrieve_token_for_user(username_or_sp_id,
455501
account[_TENANT_ID], resource)

src/command_modules/azure-cli-profile/azure/cli/command_modules/profile/custom.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414

1515
logger = get_logger(__name__)
1616

17-
_CLOUD_CONSOLE_LOGOUT_WARNING = ("Logout successful. Re-login to your initial Cloud Shell identity with"
18-
" 'az login --identity'. Login with a new identity with 'az login'.")
19-
_CLOUD_CONSOLE_LOGIN_WARNING = ("Cloud Shell is automatically authenticated under the initial account signed-in with."
20-
" Run 'az login' only if you need to use a different account")
17+
18+
_CLOUD_CONSOLE_WARNING_TEMPLATE = ("Azure Cloud Shell automatically authenticates the user account it was initially"
19+
" launched under, as a result 'az %s' is disabled.")
20+
# _CLOUD_CONSOLE_LOGOUT_WARNING = ("Logout successful. Re-login to your initial Cloud Shell identity with"
21+
# " 'az login --identity'. Login with a new identity with 'az login'.")
22+
# _CLOUD_CONSOLE_LOGIN_WARNING = ("Cloud Shell is automatically authenticated under the initial account signed-in with."
23+
# " Run 'az login' only if you need to use a different account")
2124

2225

2326
def _load_subscriptions(cli_ctx, all_clouds=False, refresh=False):
@@ -83,8 +86,8 @@ def set_active_subscription(cmd, subscription):
8386

8487
def account_clear(cmd):
8588
"""Clear all stored subscriptions. To clear individual, use 'logout'"""
86-
if in_cloud_console():
87-
logger.warning(_CLOUD_CONSOLE_LOGOUT_WARNING)
89+
# if in_cloud_console():
90+
# logger.warning(_CLOUD_CONSOLE_LOGOUT_WARNING)
8891
profile = Profile(cli_ctx=cmd.cli_ctx)
8992
profile.logout_all()
9093

@@ -94,6 +97,8 @@ def login(cmd, username=None, password=None, service_principal=None, tenant=None
9497
identity=False, identity_port=None,
9598
msi=False, msi_port=None): # will remove msi_xxx in a future release
9699
"""Log in to access Azure subscriptions"""
100+
import os
101+
import re
97102
from adal.adal_error import AdalError
98103
import requests
99104

@@ -106,12 +111,22 @@ def login(cmd, username=None, password=None, service_principal=None, tenant=None
106111

107112
profile = Profile(cli_ctx=cmd.cli_ctx, async_persist=False)
108113

114+
# if identity or msi:
115+
# if in_cloud_console():
116+
# return profile.find_subscriptions_in_cloud_console()
117+
# return profile.find_subscriptions_in_vm_with_msi(identity_port or msi_port or 50342, username)
118+
# elif in_cloud_console(): # tell users they might not need login
119+
# logger.warning(_CLOUD_CONSOLE_LOGIN_WARNING)
120+
121+
if in_cloud_console():
122+
console_tokens = os.environ.get('AZURE_CONSOLE_TOKENS', None)
123+
if console_tokens:
124+
return profile.find_subscriptions_in_cloud_console_thru_raw_token(re.split(';|,', console_tokens))
125+
logger.warning(_CLOUD_CONSOLE_WARNING_TEMPLATE, 'login')
126+
return
127+
109128
if identity or msi:
110-
if in_cloud_console():
111-
return profile.find_subscriptions_in_cloud_console()
112129
return profile.find_subscriptions_in_vm_with_msi(identity_port or msi_port or 50342, username)
113-
elif in_cloud_console(): # tell users they might not need login
114-
logger.warning(_CLOUD_CONSOLE_LOGIN_WARNING)
115130

116131
if username:
117132
if not password:
@@ -150,8 +165,11 @@ def login(cmd, username=None, password=None, service_principal=None, tenant=None
150165

151166
def logout(cmd, username=None):
152167
"""Log out to remove access to Azure subscriptions"""
168+
# if in_cloud_console():
169+
# logger.warning(_CLOUD_CONSOLE_LOGOUT_WARNING)
153170
if in_cloud_console():
154-
logger.warning(_CLOUD_CONSOLE_LOGOUT_WARNING)
171+
logger.warning(_CLOUD_CONSOLE_WARNING_TEMPLATE, 'logout')
172+
return
155173

156174
profile = Profile(cli_ctx=cmd.cli_ctx)
157175
if not username:

0 commit comments

Comments
 (0)