Added az account refresh support - Update Subscriptions#3627
Added az account refresh support - Update Subscriptions#3627listonb wants to merge 2 commits intoAzure:masterfrom
Conversation
|
@listonb, |
…on list under the active account
Codecov Report
@@ Coverage Diff @@
## master #3627 +/- ##
=========================================
+ Coverage 71.62% 72.03% +0.4%
=========================================
Files 420 422 +2
Lines 26209 26319 +110
Branches 3968 3982 +14
=========================================
+ Hits 18773 18959 +186
+ Misses 6258 6142 -116
- Partials 1178 1218 +40
Continue to review full report at Codecov.
|
| # Currently requires a default subscription selected | ||
| existingSubscriptions = self.load_cached_subscriptions() | ||
| tenants = {} | ||
| bearer = self.get_raw_token() |
There was a problem hiding this comment.
Under this flow, we should not manipulate tokens directly. Only az login uses naked token because there might have no established creds, but for refresh, we do have.
Here is the prototype I would do with simpler logics (The TODOs below skip a few trivial logics to focus on the main thing)
from azure.mgmt.resource import SubscriptionClient
subscriptions = self.load_cached_subscriptions()
client_infos = []
# TODO: eliminate redundant clients from the same tenant
for s in subscriptions:
client_infos.append((SubscriptionClient(self.get_login_credentials()[0]),s))
refreshed_subs = []
for c in client_infos:
result = list(c[0].subscriptions.list())
for r in result:
setattr(r, 'tenant_id', c[1][_TENANT_ID])
refreshed_subs += Profile._normalize_properties(c[1][_USER_ENTITY][_USER_NAME],
result,
c[1][_USER_ENTITY][_USER_TYPE]==_SERVICE_PRINCIPAL)
# TODO:
# a: extract out following common code with "find_subscriptions_on_login",
# b. handle 'tenant only accounts' (no subscriptions)
# c: ensure we maintain the active subscription
self._set_subscriptions(refreshed_subs)
return deepcopy(refreshed_subs)| "token": bearer[0][1], | ||
| "finder": SubscriptionFinder(athctx, self._creds_cache.adal_token_cache)}) | ||
|
|
||
| for tenant in tenants: |
There was a problem hiding this comment.
We should support new or removed tenants. If you decide not to do it, please open a tracking issue
Refactored az account refresh to support tenant/subscription addition/removal. Additionally will auth to new tenants. Refactored AdalAuthentication module to no longer expect a method to be passed in as an arg, but expose a in-built method for _token_retriever for backwards compatibility. This allows other portions of the CLI to take advantage of the AdalAuthentication module.
|
Looks good @listonb! @yugangw-msft what do you think? |
yugangw-msft
left a comment
There was a problem hiding this comment.
Thanks for contributing. I left a few more comments. Looks like my previous comments were not all addressed, please handle them as well.
| from azure.cli.core.adal_authentication import AdalAuthentication | ||
| from azure.mgmt.resource import SubscriptionClient | ||
| # If no subscription is currently set CLI will throw error asking user to set a subscription or login | ||
| subClient = SubscriptionClient(self.get_login_credentials()[0]) |
There was a problem hiding this comment.
Are we only refreshing the current azure account or across all of them? Please note, users could login with multiple accounts, and different service principals.
| # if none exists auto-generates a new oauth token for this tenant | ||
| self.auth_ctx_factory(tenant, self._creds_cache.adal_token_cache) | ||
| self._creds_cache.load_adal_token_cache() | ||
| token = self._creds_cache.retrieve_token_for_user(tokenRetriever[2]['userId'], |
There was a problem hiding this comment.
Are we skipping the scenario of refreshing for a service principal?
| # Attempt to pull a cred cache for each tenant | ||
| # if none exists auto-generates a new oauth token for this tenant | ||
| self.auth_ctx_factory(tenant, self._creds_cache.adal_token_cache) | ||
| self._creds_cache.load_adal_token_cache() |
There was a problem hiding this comment.
I don't believe we should touch the token cache directly.
The only time we need to touch the raw token is when we need to get refreshed tenant list by starting from the common tenant, after that the logic inside _find_using_common_tenant should be reused
| def __init__(self, token_retriever): | ||
| self._token_retriever = token_retriever | ||
| def __init__(self, token): | ||
| self.token = token |
There was a problem hiding this comment.
We should not do this. We like to use a token with guaranteed validity, so this is why we acquire one right before sending out the request on the wire. Some commands take a long time to run, say vmss with 100 instances, the change here will break it because the same token used in the subsequent polling might get expired.
|
#4091 will handle it |
az account refresh - Get oauth bearer token, refresh if needed, flush token to cache, then pull a list of all subscriptions for each tenant and flush to cache.
#919