def signed_session(self): session = super(AdalAuthentication, self).signed_session() try: scheme, token, _ = self._token_retriever() except CLIError as err: if in_cloud_console(): AdalAuthentication._log_hostname() raise err except adal.AdalError as err: # pylint: disable=no-member if in_cloud_console(): AdalAuthentication._log_hostname() if 'AADSTS70008:' in (getattr(err, 'error_response', None) or {}).get('error_description') or '': raise CLIError( "Credentials have expired due to inactivity.{}".format( " Please run 'az login'" if not in_cloud_console() else '')) raise CLIError(err) except requests.exceptions.ConnectionError as err: raise CLIError( 'Please ensure you have network connection. Error detail: ' + str(err)) header = "{} {}".format(scheme, token) session.headers['Authorization'] = header return session
def _get_token(self, sdk_resource=None): """ :param sdk_resource: `resource` converted from Track 2 SDK's `scopes` """ external_tenant_tokens = None try: scheme, token, full_token = self._token_retriever(sdk_resource) if self._external_tenant_token_retriever: external_tenant_tokens = self._external_tenant_token_retriever( sdk_resource) except CLIError as err: if in_cloud_console(): AdalAuthentication._log_hostname() raise err except adal.AdalError as err: if in_cloud_console(): AdalAuthentication._log_hostname() adal_error_handler(err) except requests.exceptions.SSLError as err: from .util import SSLERROR_TEMPLATE raise CLIError(SSLERROR_TEMPLATE.format(str(err))) except requests.exceptions.ConnectionError as err: raise CLIError( 'Please ensure you have network connection. Error detail: ' + str(err)) return scheme, token, full_token, external_tenant_tokens
def _get_token(self, sdk_resource=None): """ :param sdk_resource: `resource` converted from Track 2 SDK's `scopes` """ external_tenant_tokens = None try: scheme, token, token_entry = self._token_retriever(sdk_resource) if self._external_tenant_token_retriever: external_tenant_tokens = self._external_tenant_token_retriever(sdk_resource) except CLIError as err: if in_cloud_console(): AdalAuthentication._log_hostname() raise err except adal.AdalError as err: if in_cloud_console(): AdalAuthentication._log_hostname() adal_error_handler(err) except requests.exceptions.SSLError as err: from .util import SSLERROR_TEMPLATE raise CLIError(SSLERROR_TEMPLATE.format(str(err))) except requests.exceptions.ConnectionError as err: raise CLIError('Please ensure you have network connection. Error detail: ' + str(err)) # scheme: str. The token scheme. Should always be 'Bearer'. # token: str. The raw access token. # token_entry: dict. The full token entry. # external_tenant_tokens: [(scheme: str, token: str, token_entry: dict), ...] return scheme, token, token_entry, external_tenant_tokens
def signed_session(self, session=None): # pylint: disable=arguments-differ session = session or super(AdalAuthentication, self).signed_session() external_tenant_tokens = None try: scheme, token, _ = self._token_retriever() if self._external_tenant_token_retriever: external_tenant_tokens = self._external_tenant_token_retriever() except CLIError as err: if in_cloud_console(): AdalAuthentication._log_hostname() raise err except adal.AdalError as err: # pylint: disable=no-member if in_cloud_console(): AdalAuthentication._log_hostname() if 'AADSTS70008:' in (getattr(err, 'error_response', None) or {}).get('error_description') or '': raise CLIError("Credentials have expired due to inactivity.{}".format( " Please run 'az login'" if not in_cloud_console() else '')) raise CLIError(err) except requests.exceptions.ConnectionError as err: raise CLIError('Please ensure you have network connection. Error detail: ' + str(err)) header = "{} {}".format(scheme, token) session.headers['Authorization'] = header if external_tenant_tokens: aux_tokens = ';'.join(['{} {}'.format(scheme2, tokens2) for scheme2, tokens2, _ in external_tenant_tokens]) session.headers['x-ms-authorization-auxiliary'] = aux_tokens return session
def login(cmd, username=None, password=None, service_principal=None, tenant=None, allow_no_subscriptions=False, identity=False, identity_port=None): """Log in to access Azure subscriptions""" from adal.adal_error import AdalError import requests # quick argument usage check if (any([password, service_principal, tenant, allow_no_subscriptions]) and identity): raise CLIError("usage error: '--identity' is not applicable with other arguments") if identity_port: logger.warning("'--identity-port' is no longer required to login using managed identity." " This flag will be removed in a future release of CLI.") interactive = False profile = Profile(cli_ctx=cmd.cli_ctx, async_persist=False) if identity: if in_cloud_console(): return profile.find_subscriptions_in_cloud_console() return profile.find_subscriptions_in_vm_with_msi(username) elif in_cloud_console(): # tell users they might not need login logger.warning(_CLOUD_CONSOLE_LOGIN_WARNING) if username: if not password: try: password = prompt_pass('Password: '******'Please specify both username and password in non-interactive mode.') else: interactive = True try: subscriptions = profile.find_subscriptions_on_login( interactive, username, password, service_principal, tenant, allow_no_subscriptions=allow_no_subscriptions) except AdalError as err: # try polish unfriendly server errors if username: msg = str(err) suggestion = "For cross-check, try 'az login' to authenticate through browser." if ('ID3242:' in msg) or ('Server returned an unknown AccountType' in msg): raise CLIError("The user name might be invalid. " + suggestion) if 'Server returned error in RSTR - ErrorCode' in msg: raise CLIError("Logging in through command line is not supported. " + suggestion) raise CLIError(err) except requests.exceptions.ConnectionError as err: raise CLIError('Please ensure you have network connection. Error detail: ' + str(err)) all_subscriptions = list(subscriptions) for sub in all_subscriptions: sub['cloudName'] = sub.pop('environmentName', None) return all_subscriptions
def login(cmd, username=None, password=None, service_principal=None, tenant=None, allow_no_subscriptions=False, identity=False, use_device_code=False): """Log in to access Azure subscriptions""" from adal.adal_error import AdalError import requests # quick argument usage check if any([password, service_principal, tenant, allow_no_subscriptions]) and identity: raise CLIError("usage error: '--identity' is not applicable with other arguments") if any([password, service_principal, username, identity]) and use_device_code: raise CLIError("usage error: '--use-device-code' is not applicable with other arguments") interactive = False profile = Profile(cli_ctx=cmd.cli_ctx, async_persist=False) if identity: if in_cloud_console(): return profile.find_subscriptions_in_cloud_console() return profile.find_subscriptions_in_vm_with_msi(username) elif in_cloud_console(): # tell users they might not need login logger.warning(_CLOUD_CONSOLE_LOGIN_WARNING) if username: if not password: try: password = prompt_pass('Password: '******'Please specify both username and password in non-interactive mode.') else: interactive = True try: subscriptions = profile.find_subscriptions_on_login( interactive, username, password, service_principal, tenant, use_device_code=use_device_code, allow_no_subscriptions=allow_no_subscriptions) except AdalError as err: # try polish unfriendly server errors if username: msg = str(err) suggestion = "For cross-check, try 'az login' to authenticate through browser." if ('ID3242:' in msg) or ('Server returned an unknown AccountType' in msg): raise CLIError("The user name might be invalid. " + suggestion) if 'Server returned error in RSTR - ErrorCode' in msg: raise CLIError("Logging in through command line is not supported. " + suggestion) raise CLIError(err) except requests.exceptions.ConnectionError as err: raise CLIError('Please ensure you have network connection. Error detail: ' + str(err)) all_subscriptions = list(subscriptions) for sub in all_subscriptions: sub['cloudName'] = sub.pop('environmentName', None) return all_subscriptions
def signed_session(self, session=None): # pylint: disable=arguments-differ session = session or super(AdalAuthentication, self).signed_session() external_tenant_tokens = None try: scheme, token, _ = self._token_retriever() if self._external_tenant_token_retriever: external_tenant_tokens = self._external_tenant_token_retriever( ) except CLIError as err: if in_cloud_console(): AdalAuthentication._log_hostname() raise err except adal.AdalError as err: # pylint: disable=no-member if in_cloud_console(): AdalAuthentication._log_hostname() err = (getattr(err, 'error_response', None) or {}).get('error_description') or '' if 'AADSTS70008' in err: # all errors starting with 70008 should be creds expiration related raise CLIError( "Credentials have expired due to inactivity. {}".format( "Please run 'az login'" if not in_cloud_console() else '')) if 'AADSTS50079' in err: raise CLIError( "Configuration of your account was changed. {}".format( "Please run 'az login'" if not in_cloud_console() else '')) if 'AADSTS50173' in err: raise CLIError( "The credential data used by CLI has been expired because you might have changed or " "reset the password. {}".format( "Please clear browser's cookies and run 'az login'" if not in_cloud_console() else '')) raise CLIError(err) except requests.exceptions.SSLError as err: from .util import SSLERROR_TEMPLATE raise CLIError(SSLERROR_TEMPLATE.format(str(err))) except requests.exceptions.ConnectionError as err: raise CLIError( 'Please ensure you have network connection. Error detail: ' + str(err)) header = "{} {}".format(scheme, token) session.headers['Authorization'] = header if external_tenant_tokens: aux_tokens = ';'.join([ '{} {}'.format(scheme2, tokens2) for scheme2, tokens2, _ in external_tenant_tokens ]) session.headers['x-ms-authorization-auxiliary'] = aux_tokens return session
def login(cmd, username=None, password=None, service_principal=None, tenant=None, allow_no_subscriptions=False, identity=False, use_device_code=False, use_cert_sn_issuer=None, scopes=None, client_assertion=None): """Log in to access Azure subscriptions""" # quick argument usage check if any([password, service_principal, tenant]) and identity: raise CLIError("usage error: '--identity' is not applicable with other arguments") if any([password, service_principal, username, identity]) and use_device_code: raise CLIError("usage error: '--use-device-code' is not applicable with other arguments") if use_cert_sn_issuer and not service_principal: raise CLIError("usage error: '--use-sn-issuer' is only applicable with a service principal") if service_principal and not username: raise CLIError('usage error: --service-principal --username NAME --password SECRET --tenant TENANT') interactive = False profile = Profile(cli_ctx=cmd.cli_ctx) if identity: if in_cloud_console(): return profile.login_in_cloud_shell() return profile.login_with_managed_identity(username, allow_no_subscriptions) if in_cloud_console(): # tell users they might not need login logger.warning(_CLOUD_CONSOLE_LOGIN_WARNING) if username: if not (password or client_assertion): try: password = prompt_pass('Password: '******'Please specify both username and password in non-interactive mode.') else: interactive = True if service_principal: from azure.cli.core.auth.identity import ServicePrincipalAuth password = ServicePrincipalAuth.build_credential(password, client_assertion, use_cert_sn_issuer) subscriptions = profile.login( interactive, username, password, service_principal, tenant, scopes=scopes, use_device_code=use_device_code, allow_no_subscriptions=allow_no_subscriptions, use_cert_sn_issuer=use_cert_sn_issuer) all_subscriptions = list(subscriptions) for sub in all_subscriptions: sub['cloudName'] = sub.pop('environmentName', None) return all_subscriptions
def _get_token(self, sdk_resource=None): """ :param sdk_resource: `resource` converted from Track 2 SDK's `scopes` """ external_tenant_tokens = None try: scheme, token, full_token = self._token_retriever(sdk_resource) if self._external_tenant_token_retriever: external_tenant_tokens = self._external_tenant_token_retriever( sdk_resource) except CLIError as err: if in_cloud_console(): AdalAuthentication._log_hostname() raise err except adal.AdalError as err: # pylint: disable=no-member if in_cloud_console(): AdalAuthentication._log_hostname() err = (getattr(err, 'error_response', None) or {}).get('error_description') or str(err) if 'AADSTS70008' in err: # all errors starting with 70008 should be creds expiration related raise CLIError( "Credentials have expired due to inactivity. {}".format( "Please run 'az login'" if not in_cloud_console() else '')) if 'AADSTS50079' in err: raise CLIError( "Configuration of your account was changed. {}".format( "Please run 'az login'" if not in_cloud_console() else '')) if 'AADSTS50173' in err: raise CLIError( "The credential data used by CLI has been expired because you might have changed or " "reset the password. {}".format( "Please clear browser's cookies and run 'az login'" if not in_cloud_console() else '')) raise CLIError(err) except requests.exceptions.SSLError as err: from .util import SSLERROR_TEMPLATE raise CLIError(SSLERROR_TEMPLATE.format(str(err))) except requests.exceptions.ConnectionError as err: raise CLIError( 'Please ensure you have network connection. Error detail: ' + str(err)) return scheme, token, full_token, external_tenant_tokens
def batch_data_service_factory(cli_ctx, kwargs): import azure.batch.batch_service_client as batch import azure.batch.batch_auth as batchauth account_name = kwargs.pop('account_name', None) account_key = kwargs.pop('account_key', None) account_endpoint = kwargs.pop('account_endpoint', None) kwargs.pop('yes', None) credentials = None if not account_key: from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) # in order to use AAD auth in cloud shell mode, we will use mgmt AAD token # instead of Batch AAD token to auth if in_cloud_console(): resource = cli_ctx.cloud.endpoints.active_directory_resource_id else: resource = cli_ctx.cloud.endpoints.batch_resource_id credentials, _, _ = profile.get_login_credentials(resource=resource) else: credentials = batchauth.SharedKeyCredentials(account_name, account_key) if not account_endpoint.startswith('https://'): account_endpoint = 'https://' + account_endpoint return batch.BatchServiceClient(credentials, base_url=account_endpoint)
def aad_error_handler(error, **kwargs): """ Handle the error from AAD server returned by ADAL or MSAL. """ # https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes # Search for an error code at https://login.microsoftonline.com/error # To trigger this function for testing, simply provide an invalid scope: # az account get-access-token --scope https://my-invalid-scope from azure.cli.core.util import in_cloud_console if in_cloud_console(): import socket logger.warning( "A Cloud Shell credential problem occurred. When you report the issue with the error " "below, please mention the hostname '%s'", socket.gethostname()) error_description = error.get('error_description') # Build recommendation message login_command = _generate_login_command(**kwargs) login_message = ( # Cloud Shell uses IMDS-like interface for implicit login. If getting token/cert failed, # we let the user explicitly log in to AAD with MSAL. "Please explicitly log in with:\n{}" if error.get('error') == 'broker_error' else "To re-authenticate, please run:\n{}").format(login_command) from azure.cli.core.azclierror import AuthenticationError raise AuthenticationError(error_description, recommendation=login_message)
def _configure_credentials(self, credentials, mgmt_credentials, subscription_id): if not credentials: from azure.cli.core.util import in_cloud_console profile, subscription, endpoints = self._get_cli_profile( subscription_id) if in_cloud_console(): resource = endpoints.active_directory_resource_id else: resource = endpoints.batch_resource_id credentials, subscription_id, _ = profile.get_login_credentials( resource=resource, subscription_id=subscription) if not mgmt_credentials: try: profile, subscription, endpoints = self._get_cli_profile( subscription_id) except ValueError: pass else: mgmt_credentials, subscription_id, _ = profile.get_login_credentials( resource=endpoints.management, subscription_id=subscription) if not mgmt_credentials: try: mgmt_resource = credentials.cloud_environment.endpoints.management except AttributeError: pass else: mgmt_credentials = copy.copy(credentials) mgmt_credentials.resource = mgmt_resource mgmt_credentials.set_token() return credentials, mgmt_credentials, subscription_id
def acr_check_health( cmd, # pylint: disable useless-return vnet=None, ignore_errors=False, yes=False, registry_name=None): from azure.cli.core.util import in_cloud_console in_cloud_console = in_cloud_console() if in_cloud_console: logger.warning( "Environment checks are not supported in Azure Cloud Shell.") else: _get_docker_status_and_version(ignore_errors, yes) _get_cli_version() _check_registry_health(cmd, registry_name, ignore_errors) if vnet: _check_private_endpoint(cmd, registry_name, vnet) if not in_cloud_console: _get_helm_version(ignore_errors) _get_notary_version(ignore_errors) logger.warning(FAQ_MESSAGE)
def open_url_in_browser(url): # if we are not in cloud shell and can launch a browser, launch it with the issue draft if can_launch_browser() and not in_cloud_console(): open_page_in_browser(url) else: print("There isn't an available browser finish the setup. Please copy and paste the url" f" below in a browser to complete the configuration.\n\n{url}\n\n")
def login(username=None, password=None, service_principal=None, tenant=None, allow_no_subscriptions=False, msi=False, msi_port=DefaultStr(50342)): """Log in to access Azure subscriptions""" import os import re from adal.adal_error import AdalError import requests # quick argument usage check if (any([username, password, service_principal, tenant, allow_no_subscriptions]) and any([msi, not getattr(msi_port, 'is_default', None)])): raise CLIError("usage error: '--msi/--msi-port' are not applicable with other arguments") interactive = False profile = Profile() if in_cloud_console(): console_tokens = os.environ.get('AZURE_CONSOLE_TOKENS', None) if console_tokens: return profile.find_subscriptions_in_cloud_console(re.split(';|,', console_tokens)) logger.warning(_CLOUD_CONSOLE_WARNING_TEMPLATE, 'login') return if msi: return profile.find_subscriptions_in_vm_with_msi(msi_port) if username: if not password: try: password = prompt_pass('Password: '******'Please specify both username and password in non-interactive mode.') else: interactive = True try: subscriptions = profile.find_subscriptions_on_login( interactive, username, password, service_principal, tenant, allow_no_subscriptions=allow_no_subscriptions) except AdalError as err: # try polish unfriendly server errors if username: msg = str(err) suggestion = "For cross-check, try 'az login' to authenticate through browser." if ('ID3242:' in msg) or ('Server returned an unknown AccountType' in msg): raise CLIError("The user name might be invalid. " + suggestion) if 'Server returned error in RSTR - ErrorCode' in msg: raise CLIError("Logging in through command line is not supported. " + suggestion) raise CLIError(err) except requests.exceptions.ConnectionError as err: raise CLIError('Please ensure you have network connection. Error detail: ' + str(err)) all_subscriptions = list(subscriptions) for sub in all_subscriptions: sub['cloudName'] = sub.pop('environmentName', None) return all_subscriptions
def _retrieve_token(): if in_cloud_console() and account[_USER_ENTITY].get(_CLOUD_SHELL_ID): return self._get_token_from_cloud_shell(resource) if user_type == _USER: return self._creds_cache.retrieve_token_for_user(username_or_sp_id, account[_TENANT_ID], resource) return self._creds_cache.retrieve_token_for_service_principal(username_or_sp_id, resource)
def batch_data_service_factory(cli_ctx, kwargs): import azure.batch.batch_service_client as batch import azure.batch.batch_auth as batchauth account_name = kwargs.pop('account_name', None) account_key = kwargs.pop('account_key', None) account_endpoint = kwargs.pop('account_endpoint', None) kwargs.pop('yes', None) credentials = None if not account_key: from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) # in order to use AAD auth in cloud shell mode, we will use mgmt AAD token # instead of Batch AAD token to auth if in_cloud_console(): resource = cli_ctx.cloud.endpoints.active_directory_resource_id else: resource = cli_ctx.cloud.endpoints.batch_resource_id credentials, _, _ = profile.get_login_credentials(resource=resource) else: # Verify all values are populated and display readable error if not all([account_name, account_key, account_endpoint]): raise ValueError( 'usage error: --account-name NAME --account-key KEY --account-endpoint ENDPOINT' ) credentials = batchauth.SharedKeyCredentials(account_name, account_key) if not (account_endpoint.startswith('https://') or account_endpoint.startswith('http://')): account_endpoint = 'https://' + account_endpoint return batch.BatchServiceClient(credentials, batch_url=account_endpoint.rstrip('/'))
def _retrieve_token(): if in_cloud_console() and account[_USER_ENTITY].get(_CLOUD_SHELL_ID): return self._get_token_from_cloud_shell(resource) if user_type == _USER: return self._creds_cache.retrieve_token_for_user(username_or_sp_id, account[_TENANT_ID], resource) return self._creds_cache.retrieve_token_for_service_principal(username_or_sp_id, resource)
def batch_data_service_factory(cli_ctx, kwargs): import azure.batch.batch_service_client as batch import azure.batch.batch_auth as batchauth account_name = kwargs.pop('account_name', None) account_key = kwargs.pop('account_key', None) account_endpoint = kwargs.pop('account_endpoint', None) kwargs.pop('yes', None) credentials = None if not account_key: from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) # in order to use AAD auth in cloud shell mode, we will use mgmt AAD token # instead of Batch AAD token to auth if in_cloud_console(): resource = cli_ctx.cloud.endpoints.active_directory_resource_id else: resource = cli_ctx.cloud.endpoints.batch_resource_id credentials, _, _ = profile.get_login_credentials(resource=resource) else: credentials = batchauth.SharedKeyCredentials(account_name, account_key) if not (account_endpoint.startswith('https://') or account_endpoint.startswith('http://')): account_endpoint = 'https://' + account_endpoint return batch.BatchServiceClient(credentials, batch_url=account_endpoint.rstrip('/'))
def get_raw_token(self, resource=None, subscription=None): account = self.get_subscription(subscription) user_type = account[_USER_ENTITY][_USER_TYPE] username_or_sp_id = account[_USER_ENTITY][_USER_NAME] resource = resource or self.cli_ctx.cloud.endpoints.active_directory_resource_id identity_type, identity_id = Profile._try_parse_msi_account_name( account) if identity_type: msi_creds = MsiAccountTypes.msi_auth_factory( identity_type, identity_id, resource) msi_creds.set_token() token_entry = msi_creds.token creds = (token_entry['token_type'], token_entry['access_token'], token_entry) elif in_cloud_console() and account[_USER_ENTITY].get(_CLOUD_SHELL_ID): creds = self._get_token_from_cloud_shell(resource) elif user_type == _USER: creds = self._creds_cache.retrieve_token_for_user( username_or_sp_id, account[_TENANT_ID], resource) else: creds = self._creds_cache.retrieve_token_for_service_principal( username_or_sp_id, resource) return (creds, str(account[_SUBSCRIPTION_ID]), str(account[_TENANT_ID]))
def login(username=None, password=None, service_principal=None, tenant=None, allow_no_subscriptions=False, msi=False, msi_port=DefaultStr(50342)): """Log in to access Azure subscriptions""" import os import re from adal.adal_error import AdalError import requests # quick argument usage check if (any([password, service_principal, tenant, allow_no_subscriptions]) and any([msi, not getattr(msi_port, 'is_default', None)])): raise CLIError("usage error: '--msi/--msi-port' are not applicable with other arguments") interactive = False profile = Profile() if in_cloud_console(): console_tokens = os.environ.get('AZURE_CONSOLE_TOKENS', None) if console_tokens: return profile.find_subscriptions_in_cloud_console(re.split(';|,', console_tokens)) logger.warning(_CLOUD_CONSOLE_WARNING_TEMPLATE, 'login') return if msi: return profile.find_subscriptions_in_vm_with_msi(msi_port, username) if username: if not password: try: password = prompt_pass('Password: '******'Please specify both username and password in non-interactive mode.') else: interactive = True try: subscriptions = profile.find_subscriptions_on_login( interactive, username, password, service_principal, tenant, allow_no_subscriptions=allow_no_subscriptions) except AdalError as err: # try polish unfriendly server errors if username: msg = str(err) suggestion = "For cross-check, try 'az login' to authenticate through browser." if ('ID3242:' in msg) or ('Server returned an unknown AccountType' in msg): raise CLIError("The user name might be invalid. " + suggestion) if 'Server returned error in RSTR - ErrorCode' in msg: raise CLIError("Logging in through command line is not supported. " + suggestion) raise CLIError(err) except requests.exceptions.ConnectionError as err: raise CLIError('Please ensure you have network connection. Error detail: ' + str(err)) all_subscriptions = list(subscriptions) for sub in all_subscriptions: sub['cloudName'] = sub.pop('environmentName', None) return all_subscriptions
def account_clear(cmd): """Clear all stored subscriptions. To clear individual, use 'logout'""" _remove_adal_token_cache() if in_cloud_console(): logger.warning(_CLOUD_CONSOLE_LOGOUT_WARNING) profile = Profile(cli_ctx=cmd.cli_ctx) profile.logout_all()
def _prompt_issue(recent_command_list): if recent_command_list: max_idx = len(recent_command_list) - 1 ans = -1 help_string = 'Please choose between 0 and {}, or enter q to quit: '.format( max_idx) while ans < 0 or ans > max_idx: try: ans = prompt(_MSG_CMD_ISSUE.format(max_idx), help_string=help_string) if ans.lower() in ["q", "quit"]: ans = ans.lower() break ans = int(ans) except ValueError: logger.warning(help_string) ans = -1 else: ans = None help_string = 'Please choose between Y and N: ' while not ans: ans = prompt(_MSG_ISSUE, help_string=help_string) if ans.lower() not in ["y", "n", "yes", "no", "q"]: ans = None continue # strip to short form ans = ans[0].lower() if ans else None if ans in ["y", "n"]: if ans == "y": browser_instruction, url, issue_body = _build_issue_info_tup() else: return False else: if ans in ["q", "quit"]: return False if ans == 0: browser_instruction, url, issue_body = _build_issue_info_tup() else: browser_instruction, url, issue_body = _build_issue_info_tup( recent_command_list[ans]) logger.info(issue_body) print(browser_instruction) # if we are not in cloud shell and can launch a browser, launch it with the issue draft if can_launch_browser() and not in_cloud_console(): open_page_in_browser(url) else: print( "There isn't an available browser to create an issue draft. You can copy and paste the url" " below in a browser to submit.\n\n{}\n\n".format(url)) return True
def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument logger.debug( "MSIAuthenticationWrapper.get_token: scopes=%r, kwargs=%r", scopes, kwargs) if 'data' in kwargs: from azure.cli.core.util import in_cloud_console if in_cloud_console(): # Use MSAL to get VM SSH certificate import msal from .util import check_result, build_sdk_access_token from .identity import AZURE_CLI_CLIENT_ID app = msal.PublicClientApplication( AZURE_CLI_CLIENT_ID, # Use a real client_id, so that cache would work # TODO: This PoC does not currently maintain a token cache; # Ideally we should reuse the real MSAL app object which has cache configured. # token_cache=..., ) result = app.acquire_token_interactive(list(scopes), prompt="none", data=kwargs["data"]) check_result(result, scopes=scopes) return build_sdk_access_token(result) from azure.cli.core.azclierror import AuthenticationError raise AuthenticationError( "VM SSH currently doesn't support managed identity.") # Use msrestazure to get access token resource = scopes_to_resource(_normalize_scopes(scopes)) if resource: # If available, use resource provided by SDK self.resource = resource self.set_token() # VM managed identity endpoint 2018-02-01 token entry sample: # curl "http://169.254.169.254:80/metadata/identity/oauth2/token?resource=https://management.core.windows.net/&api-version=2018-02-01" -H "Metadata: true" # { # "access_token": "eyJ0eXAiOiJKV...", # "client_id": "da95e381-d7ab-4fdc-8047-2457909c723b", # "expires_in": "86386", # "expires_on": "1605238724", # "ext_expires_in": "86399", # "not_before": "1605152024", # "resource": "https://management.core.windows.net/", # "token_type": "Bearer" # } # App Service managed identity endpoint 2017-09-01 token entry sample: # curl "${MSI_ENDPOINT}?resource=https://management.core.windows.net/&api-version=2017-09-01" -H "secret: ${MSI_SECRET}" # { # "access_token": "eyJ0eXAiOiJKV...", # "expires_on":"11/05/2021 15:18:31 +00:00", # "resource":"https://management.core.windows.net/", # "token_type":"Bearer", # "client_id":"df45d93a-de31-47ca-acef-081ca60d1a83" # } return AccessToken(self.token['access_token'], _normalize_expires_on(self.token['expires_on']))
def acr_login( cmd, registry_name, resource_group_name=None, # pylint: disable=unused-argument tenant_suffix=None, username=None, password=None): from azure.cli.core.util import in_cloud_console if in_cloud_console(): raise CLIError( 'This command requires running the docker daemon, which is not supported in Azure Cloud Shell.' ) docker_command, _ = get_docker_command() login_server, username, password = get_login_credentials( cmd=cmd, registry_name=registry_name, tenant_suffix=tenant_suffix, username=username, password=password) # warn casing difference caused by ACR normalizing to lower on login_server parts = login_server.split('.') if registry_name != parts[0] and registry_name.lower() == parts[0]: logger.warning( 'Uppercase characters are detected in the registry name. When using its server url in ' 'docker commands, to avoid authentication errors, use all lowercase.' ) from subprocess import PIPE, Popen p = Popen([ docker_command, "login", "--username", username, "--password", password, login_server ], stderr=PIPE) _, stderr = p.communicate() if stderr: if b'error storing credentials' in stderr and b'stub received bad data' in stderr \ and _check_wincred(login_server): # Retry once after disabling wincred p = Popen([ docker_command, "login", "--username", username, "--password", password, login_server ]) p.wait() else: if b'--password-stdin' in stderr: errors = [ err for err in stderr.decode().split('\n') if '--password-stdin' not in err ] stderr = '\n'.join(errors).encode() import sys output = getattr(sys.stderr, 'buffer', sys.stderr) output.write(stderr)
def _build_issue_info_tup(command_log_file=None): format_dict = { "command_name": "", "errors_string": "", "executed_command": "" } is_ext = False ext_name = None # Get command information, if applicable if command_log_file: command_name = command_log_file.metadata_tup.cmd format_dict["command_name"] = command_name if command_log_file.command_data_dict: errors_list = command_log_file.command_data_dict.get("errors", []) executed_command = command_log_file.command_data_dict.get( "command_args", "") extension_name = command_log_file.command_data_dict.get( "extension_name", "") extension_version = command_log_file.command_data_dict.get( "extension_version", "") extension_info = "" if extension_name: extension_info = "\nExtension Name: {}. Version: {}.".format( extension_name, extension_version) is_ext = True ext_name = extension_name format_dict["errors_string"] = ''.join(errors_list) format_dict[ "executed_command"] = "az " + executed_command if executed_command else executed_command format_dict["command_name"] += extension_info # Get other system information format_dict["cli_version"] = _get_az_version_summary() format_dict["python_info"] = "Python {}".format(platform.python_version()) platform_info = "{} (Cloud Shell)".format( platform.platform()) if in_cloud_console() else platform.platform() format_dict["platform"] = platform_info format_dict["auto_gen_comment"] = _AUTO_GEN_COMMENT from azure.cli.core._environment import _ENV_AZ_INSTALLER format_dict["installer"] = "Installer: {}".format( os.getenv(_ENV_AZ_INSTALLER) or '') pretty_url_name = _get_extension_repo_url( ext_name) if is_ext else _CLI_ISSUES_URL issue_body = _ISSUES_TEMPLATE.format(**format_dict) formatted_issues_url = _get_issue_url(command_log_file, issue_body, is_ext, ext_name) logger.debug("Total formatted url length is %s", len(formatted_issues_url)) return _OPEN_BROWSER_INSTRUCTION.format(pretty_url_name, _CLI_ISSUES_URL, _EXTENSIONS_ISSUES_URL), \ formatted_issues_url, issue_body
def logout(username=None): """Log out to remove access to Azure subscriptions""" if in_cloud_console(): raise CLIError(_CLOUD_CONSOLE_ERR_TEMPLATE.format('logout')) profile = Profile() if not username: username = profile.get_current_account_user() profile.logout(username)
def _generate_login_message(**kwargs): from azure.cli.core.util import in_cloud_console login_command = _generate_login_command(**kwargs) msg = "To re-authenticate, please {}".format( "refresh Azure Portal." if in_cloud_console( ) else "run:\n{}".format(login_command)) return msg
def logout(cmd, username=None): """Log out to remove access to Azure subscriptions""" if in_cloud_console(): logger.warning(_CLOUD_CONSOLE_LOGOUT_WARNING) profile = Profile(cli_ctx=cmd.cli_ctx) if not username: username = profile.get_current_account_user() profile.logout(username)
def logout(cmd, username=None): """Log out to remove access to Azure subscriptions""" if in_cloud_console(): logger.warning(_CLOUD_CONSOLE_LOGOUT_WARNING) profile = Profile(cli_ctx=cmd.cli_ctx) if not username: username = profile.get_current_account_user() profile.logout(username)
def _ssl_context(): if sys.version_info < (3, 4) or (in_cloud_console() and platform.system() == 'Windows'): try: return ssl.SSLContext( ssl.PROTOCOL_TLS) # added in python 2.7.13 and 3.6 except AttributeError: return ssl.SSLContext(ssl.PROTOCOL_TLSv1) return ssl.create_default_context()
def _check_health_environment(ignore_errors, yes): from azure.cli.core.util import in_cloud_console if in_cloud_console(): logger.warning( "Environment checks are not supported in Azure Cloud Shell.") return _get_docker_status_and_version(ignore_errors, yes) _get_cli_version() _get_helm_version(ignore_errors)
def retrieve_token_for_user(self, username, tenant, resource): context = self._auth_ctx_factory(self._ctx, tenant, cache=self.adal_token_cache) token_entry = context.acquire_token(resource, username, _CLIENT_ID) if not token_entry: raise CLIError("Could not retrieve token from local cache.{}".format( " Please run 'az login'." if not in_cloud_console() else '')) if self.adal_token_cache.has_state_changed: self.persist_cached_creds() return (token_entry[_TOKEN_ENTRY_TOKEN_TYPE], token_entry[_ACCESS_TOKEN], token_entry)
def retrieve_token_for_user(self, username, tenant, resource): context = self._auth_ctx_factory(tenant, cache=self.adal_token_cache) token_entry = context.acquire_token(resource, username, _CLIENT_ID) if not token_entry: raise CLIError("Could not retrieve token from local cache.{}".format( " Please run 'az login'." if not in_cloud_console() else '')) if self.adal_token_cache.has_state_changed: self.persist_cached_creds() return (token_entry[_TOKEN_ENTRY_TOKEN_TYPE], token_entry[_ACCESS_TOKEN], token_entry)
def ssl_context(): """Returns an SSL context appropriate for the python version and environment.""" if sys.version_info < (3, 4) or (in_cloud_console() and platform.system() == "Windows"): try: # added in python 2.7.13 and 3.6 return ssl.SSLContext(ssl.PROTOCOL_TLS) except AttributeError: return ssl.SSLContext(ssl.PROTOCOL_TLSv1) return ssl.create_default_context()
def logout(username=None): """Log out to remove access to Azure subscriptions""" if in_cloud_console(): logger.warning(_CLOUD_CONSOLE_WARNING_TEMPLATE, 'logout') return profile = Profile() if not username: username = profile.get_current_account_user() profile.logout(username)
def logout(username=None): """Log out to remove access to Azure subscriptions""" if in_cloud_console(): logger.warning(_CLOUD_CONSOLE_WARNING_TEMPLATE, 'logout') return profile = Profile() if not username: username = profile.get_current_account_user() profile.logout(username)
def aad_error_handler(error: dict): """ Handle the error from AAD server returned by ADAL or MSAL. """ login_message = ("To re-authenticate, please {}. If the problem persists, " "please contact your tenant administrator." .format("refresh Azure Portal" if in_cloud_console() else "run `az login`")) # https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes # Search for an error code at https://login.microsoftonline.com/error msg = error.get('error_description') from azure.cli.core.azclierror import AuthenticationError raise AuthenticationError(msg, login_message)
def signed_session(self): session = super(AdalAuthentication, self).signed_session() try: scheme, token, _ = self._token_retriever() except CLIError as err: if in_cloud_console(): AdalAuthentication._log_hostname() raise err except adal.AdalError as err: # pylint: disable=no-member if in_cloud_console(): AdalAuthentication._log_hostname() if 'AADSTS70008:' in (getattr(err, 'error_response', None) or {}).get('error_description') or '': raise CLIError("Credentials have expired due to inactivity.{}".format( " Please run 'az login'" if not in_cloud_console() else '')) raise CLIError(err) except requests.exceptions.ConnectionError as err: raise CLIError('Please ensure you have network connection. Error detail: ' + str(err)) header = "{} {}".format(scheme, token) session.headers['Authorization'] = header return session
def acr_login(cmd, registry_name, resource_group_name=None, # pylint: disable=unused-argument tenant_suffix=None, username=None, password=None): from azure.cli.core.util import in_cloud_console if in_cloud_console(): raise CLIError('This command requires running the docker daemon, which is not supported in Azure Cloud Shell.') docker_command = _get_docker_command() login_server, username, password = get_login_credentials( cmd=cmd, registry_name=registry_name, tenant_suffix=tenant_suffix, username=username, password=password) from subprocess import PIPE, Popen p = Popen([docker_command, "login", "--username", username, "--password", password, login_server], stderr=PIPE) _, stderr = p.communicate() if stderr: if b'error storing credentials' in stderr and b'stub received bad data' in stderr \ and _check_wincred(login_server): # Retry once after disabling wincred p = Popen([docker_command, "login", "--username", username, "--password", password, login_server]) p.wait() else: if b'--password-stdin' in stderr: errors = [err for err in stderr.decode().split('\n') if '--password-stdin' not in err] stderr = '\n'.join(errors).encode() import sys output = getattr(sys.stderr, 'buffer', sys.stderr) output.write(stderr)
def login_account(cmd, client, resource_group_name, account_name, shared_key_auth=False, show=False): account = client.get(resource_group_name=resource_group_name, account_name=account_name) cmd.cli_ctx.config.set_value('batch', 'account', account.name) cmd.cli_ctx.config.set_value('batch', 'endpoint', 'https://{}/'.format(account.account_endpoint)) if shared_key_auth: keys = client.get_keys(resource_group_name=resource_group_name, account_name=account_name) cmd.cli_ctx.config.set_value('batch', 'auth_mode', 'shared_key') cmd.cli_ctx.config.set_value('batch', 'access_key', keys.primary) if show: return { 'account': account.name, 'endpoint': 'https://{}/'.format(account.account_endpoint), 'primaryKey': keys.primary, 'secondaryKey': keys.secondary } else: cmd.cli_ctx.config.set_value('batch', 'auth_mode', 'aad') if show: if in_cloud_console(): resource = cmd.cli_ctx.cloud.endpoints.active_directory_resource_id else: resource = cmd.cli_ctx.cloud.endpoints.batch_resource_id profile = Profile(cli_ctx=cmd.cli_ctx) creds, subscription, tenant = profile.get_raw_token(resource=resource) return { 'account': account.name, 'endpoint': 'https://{}/'.format(account.account_endpoint), 'tokenType': creds[0], 'accessToken': creds[1], 'expiresOn': creds[2]['expiresOn'], 'subscription': subscription, 'tenant': tenant, 'resource': resource }
def acr_login(cmd, registry_name, resource_group_name=None, username=None, password=None): from azure.cli.core.util import in_cloud_console if in_cloud_console(): raise CLIError('This command requires running the docker daemon, which is not supported in Azure Cloud Shell.') from subprocess import PIPE, Popen docker_command = _get_docker_command() login_server, username, password = get_login_credentials( cli_ctx=cmd.cli_ctx, registry_name=registry_name, resource_group_name=resource_group_name, username=username, password=password) p = Popen([docker_command, "login", "--username", username, "--password", password, login_server], stderr=PIPE) _, stderr = p.communicate() if stderr: if b'error storing credentials' in stderr and b'stub received bad data' in stderr \ and _check_wincred(login_server): # Retry once after disabling wincred p = Popen([docker_command, "login", "--username", username, "--password", password, login_server]) p.wait() elif b'--password-stdin' in stderr: pass else: import sys output = getattr(sys.stderr, 'buffer', sys.stderr) output.write(stderr)
def get_raw_token(self, resource=None, subscription=None): account = self.get_subscription(subscription) user_type = account[_USER_ENTITY][_USER_TYPE] username_or_sp_id = account[_USER_ENTITY][_USER_NAME] resource = resource or self.cli_ctx.cloud.endpoints.active_directory_resource_id identity_type, identity_id = Profile._try_parse_msi_account_name(account) if identity_type: msi_creds = MsiAccountTypes.msi_auth_factory(identity_type, identity_id, resource) msi_creds.set_token() token_entry = msi_creds.token creds = (token_entry['token_type'], token_entry['access_token'], token_entry) elif in_cloud_console() and account[_USER_ENTITY].get(_CLOUD_SHELL_ID): creds = self._get_token_from_cloud_shell(resource) elif user_type == _USER: creds = self._creds_cache.retrieve_token_for_user(username_or_sp_id, account[_TENANT_ID], resource) else: creds = self._creds_cache.retrieve_token_for_service_principal(username_or_sp_id, resource) return (creds, str(account[_SUBSCRIPTION_ID]), str(account[_TENANT_ID]))
def find_subscriptions_on_login(self, interactive, username, password, is_service_principal, tenant, use_device_code=False, allow_no_subscriptions=False, subscription_finder=None): from azure.cli.core._debug import allow_debug_adal_connection allow_debug_adal_connection() subscriptions = [] if not subscription_finder: subscription_finder = SubscriptionFinder(self.cli_ctx, self.auth_ctx_factory, self._creds_cache.adal_token_cache) if interactive: if not use_device_code and (in_cloud_console() or not can_launch_browser()): logger.info('Detect no GUI is available, so fall back to device code') use_device_code = True if not use_device_code: try: authority_url, _ = _get_authority_url(self.cli_ctx, tenant) subscriptions = subscription_finder.find_through_authorization_code_flow( tenant, self._ad_resource_uri, authority_url) except RuntimeError: use_device_code = True logger.warning('Not able to launch a browser to log you in, falling back to device code...') if use_device_code: subscriptions = subscription_finder.find_through_interactive_flow( tenant, self._ad_resource_uri) else: if is_service_principal: if not tenant: raise CLIError('Please supply tenant using "--tenant"') sp_auth = ServicePrincipalAuth(password) subscriptions = subscription_finder.find_from_service_principal_id( username, sp_auth, tenant, self._ad_resource_uri) else: subscriptions = subscription_finder.find_from_user_account( username, password, tenant, self._ad_resource_uri) if not allow_no_subscriptions and not subscriptions: raise CLIError("No subscriptions were found for '{}'. If this is expected, use " "'--allow-no-subscriptions' to have tenant level accesses".format( username)) if is_service_principal: self._creds_cache.save_service_principal_cred(sp_auth.get_entry_to_persist(username, tenant)) if self._creds_cache.adal_token_cache.has_state_changed: self._creds_cache.persist_cached_creds() if allow_no_subscriptions: t_list = [s.tenant_id for s in subscriptions] bare_tenants = [t for t in subscription_finder.tenants if t not in t_list] profile = Profile(cli_ctx=self.cli_ctx) subscriptions = profile._build_tenant_level_accounts(bare_tenants) # pylint: disable=protected-access if not subscriptions: return [] consolidated = self._normalize_properties(subscription_finder.user_id, subscriptions, is_service_principal) self._set_subscriptions(consolidated) # use deepcopy as we don't want to persist these changes to file. return deepcopy(consolidated)
def account_clear(cmd): """Clear all stored subscriptions. To clear individual, use 'logout'""" if in_cloud_console(): logger.warning(_CLOUD_CONSOLE_LOGOUT_WARNING) profile = Profile(cli_ctx=cmd.cli_ctx) profile.logout_all()