def test_get_raw_token(self, mock_get_token, mock_read_cred_file): some_token_type = 'Bearer' mock_read_cred_file.return_value = [Test_Profile.token_entry1] mock_get_token.return_value = (some_token_type, Test_Profile.raw_token1, Test_Profile.token_entry1) # setup storage_mock = {'subscriptions': None} profile = Profile(storage_mock, use_global_creds_cache=False) consolidated = Profile._normalize_properties(self.user1, [self.subscription1], False) profile._set_subscriptions(consolidated) # action creds, sub, tenant = profile.get_raw_token(resource='https://foo') # verify self.assertEqual(creds[0], self.token_entry1['tokenType']) self.assertEqual(creds[1], self.raw_token1) # the last in the tuple is the whole token entry which has several fields self.assertEqual(creds[2]['expiresOn'], self.token_entry1['expiresOn']) mock_get_token.assert_called_once_with(mock.ANY, self.user1, self.tenant_id, 'https://foo') self.assertEqual(mock_get_token.call_count, 1) self.assertEqual(sub, '1') self.assertEqual(tenant, self.tenant_id)
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: 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 { 'tokenType': creds[0], 'accessToken': creds[1], 'expiresOn': creds[2]['expiresOn'], 'subscription': subscription, 'tenant': tenant, 'resource': resource }
def get_access_token(subscription=None, resource=None): ''' get AAD token to access to a specified resource :param resource: Azure resource endpoints. Default to Azure Resource Manager Use 'az cloud show' command for other Azure resources ''' resource = (resource or get_active_cloud().endpoints.active_directory_resource_id) profile = Profile() creds, subscription, tenant = profile.get_raw_token(resource, subscription=subscription) return { 'tokenType': creds[0], 'accessToken': creds[1], 'expiresOn': creds[2]['expiresOn'], 'subscription': subscription, 'tenant': tenant }
def _get_aad_token(cmd, resource=None): ''' get AAD token to access to a specified resource :param resource: Azure resource endpoints. Default to Azure Resource Manager Use 'az cloud show' command for other Azure resources ''' resource = (resource or cmd.cli_ctx.cloud.endpoints.active_directory_resource_id) profile = Profile(cli_ctx=cmd.cli_ctx) creds, subscription, tenant = profile.get_raw_token(subscription=None, resource=resource) return { 'tokenType': creds[0], 'accessToken': creds[1], 'expiresOn': creds[2].get('expiresOn', 'N/A'), 'subscription': subscription, 'tenant': tenant }
def get_access_token(subscription=None, resource=None): ''' get AAD token to access to a specified resource :param resource: Azure resource endpoints. Default to Azure Resource Manager Use 'az cloud show' command for other Azure resources ''' resource = (resource or get_active_cloud().endpoints.active_directory_resource_id) profile = Profile() creds, subscription, tenant = profile.get_raw_token( resource, subscription=subscription) return { 'tokenType': creds[0], 'accessToken': creds[1], 'expiresOn': creds[2]['expiresOn'], 'subscription': subscription, 'tenant': tenant }
def getTemplate(cmd, templateId, solutionStorageConnectionString=None): """Gets details about the specified template from the gallery. templateId: The unique id of the template. """ profile = Profile(cli_ctx=cmd.cli_ctx) auth_token = profile.get_raw_token() creds = authentication.BasicTokenAuthentication( {'access_token': auth_token[0][1]}) ciqsapi = ciqs_api.CiqsApi(creds=creds, base_url=api.getEndpoint()) try: logger.info("Sending request.") template = ciqsapi.get_api_gallery_by_template_id( templateId, solution_storage_connection_string=solutionStorageConnectionString) except msrest.exceptions.HttpOperationError as e: message = e.response.json() raise CLIError(message) return template
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 _send_request(cmd, resource_group_name, grafana_name, http_method, path, body=None, raise_for_error_status=True): endpoint = grafana_endpoints.get(grafana_name) if not endpoint: grafana = show_grafana(cmd, grafana_name, resource_group_name) endpoint = grafana.properties.endpoint grafana_endpoints[grafana_name] = endpoint from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cmd.cli_ctx) # this might be a cross tenant scenario, so pass subscription to get_raw_token subscription = get_subscription_id(cmd.cli_ctx) amg_first_party_app = ("7f525cdc-1f08-4afa-af7c-84709d42f5d3" if "-ppe." in cmd.cli_ctx.cloud.endpoints.active_directory else "ce34e7e5-485f-4d76-964f-b3d2b16d1e4f") creds, _, _ = profile.get_raw_token(subscription=subscription, resource=amg_first_party_app) headers = { "content-type": "application/json", "authorization": "Bearer " + creds[1] } # TODO: handle re-try on 429 response = requests.request( http_method, url=endpoint + path, headers=headers, json=body, timeout=60, verify=(not should_disable_connection_verify())) if response.status_code >= 400: if raise_for_error_status: logger.warning(str(response.content)) response.raise_for_status() # TODO: log headers, requests and response return response
def get_azure_cli_auth_token(resource): from adal import AuthenticationContext import os try: # this makes it cleaner, but in case azure cli is not present on virtual env, # but cli exists on computer, we can try and manually get the token from the cache from azure.cli.core._profile import Profile from azure.cli.core._session import ACCOUNT from azure.cli.core._environment import get_config_dir azure_folder = get_config_dir() ACCOUNT.load(os.path.join(azure_folder, "azureProfile.json")) profile = Profile(storage=ACCOUNT) token_data = profile.get_raw_token()[0][2] client_id = token_data["_clientId"] refresh_token = token_data["refreshToken"] logger.info(f"Found existing AZ CLI profile for {token_data[0]['userId']}") except ModuleNotFoundError: try: import os import json folder = os.getenv("AZURE_CONFIG_DIR", None) or os.path.expanduser(os.path.join("~", ".azure")) token_path = os.path.join(folder, "accessTokens.json") with open(token_path) as f: data = json.load(f) client_id = data[0]["_clientId"] refresh_token = data[0]["refreshToken"] logger.info(f"Found existing AZ CLI profile for {data[0]['userId']}") except Exception: return None return AuthenticationContext(f"https://login.microsoftonline.com/common").acquire_token_with_refresh_token( refresh_token, client_id, f"https://{resource}.kusto.windows.net" )["accessToken"]
def get_access_token(cmd, subscription=None, resource=None, resource_type=None): ''' get AAD token to access to a specified resource :param resource: Azure resource endpoints. Default to Azure Resource Manager :param resource-type: Name of Azure resource endpoints. Can be used instead of resource. Use 'az cloud show' command for other Azure resources ''' if resource is None and resource_type is not None: endpoints_attr_name = cloud_resource_type_mappings[resource_type] resource = getattr(cmd.cli_ctx.cloud.endpoints, endpoints_attr_name) else: resource = (resource or cmd.cli_ctx.cloud.endpoints.active_directory_resource_id) profile = Profile(cli_ctx=cmd.cli_ctx) creds, subscription, tenant = profile.get_raw_token(subscription=subscription, resource=resource) return { 'tokenType': creds[0], 'accessToken': creds[1], 'expiresOn': creds[2].get('expiresOn', 'N/A'), 'subscription': subscription, 'tenant': tenant }
def deleteDeployment(cmd, deploymentId, subscription=None): """Deletes a deployment. deploymentId: The unique id created at the time the deployment was made. subscription[optional]: Provides an alternate subscripton to use if desired. """ if subscription is None: subscription = get_subscription_id(cmd.cli_ctx) logger.info("Using default subscription: " + subscription) profile = Profile(cli_ctx=cmd.cli_ctx) auth_token = profile.get_raw_token(subscription=subscription) creds = authentication.BasicTokenAuthentication( {'access_token': auth_token[0][1]}) ciqsapi = ciqs_api.CiqsApi(creds=creds, base_url=api.getEndpoint()) try: logger.info("Sending request.") deleteResponse = ciqsapi.delete_api_deployments_by_subscription_id_by_deployment_id( subscription, deploymentId) except msrest.exceptions.HttpOperationError as e: message = e.response.json() raise CLIError(message) return deleteResponse
def get_access_token(cmd, subscription=None, resource=None, resource_type=None, tenant=None): """ get AAD token to access to a specified resource :param resource: Azure resource endpoints. Default to Azure Resource Manager :param resource-type: Name of Azure resource endpoints. Can be used instead of resource. Use 'az cloud show' command for other Azure resources """ if resource is None and resource_type is not None: endpoints_attr_name = cloud_resource_type_mappings[resource_type] resource = getattr(cmd.cli_ctx.cloud.endpoints, endpoints_attr_name) else: resource = (resource or cmd.cli_ctx.cloud.endpoints.active_directory_resource_id) profile = Profile(cli_ctx=cmd.cli_ctx) creds, subscription, tenant = profile.get_raw_token( subscription=subscription, resource=resource, tenant=tenant) token_entry = creds[2] # MSIAuthentication's token entry has `expires_on`, while ADAL's token entry has `expiresOn` # Unify to ISO `expiresOn`, like "2020-06-30 06:14:41" if 'expires_on' in token_entry: from datetime import datetime # https://docs.python.org/3.8/library/datetime.html#strftime-and-strptime-format-codes token_entry['expiresOn'] = datetime.fromtimestamp(int(token_entry['expires_on']))\ .strftime("%Y-%m-%d %H:%M:%S.%f") result = { 'tokenType': creds[0], 'accessToken': creds[1], 'expiresOn': creds[2].get('expiresOn', 'N/A'), 'tenant': tenant } if subscription: result['subscription'] = subscription return result
def _get_azure_cli_auth_token() -> dict: """ Try to get the az cli authenticated token :return: refresh token """ import os try: # this makes it cleaner, but in case azure cli is not present on virtual env, # but cli exists on computer, we can try and manually get the token from the cache from azure.cli.core._profile import Profile from azure.cli.core._session import ACCOUNT from azure.cli.core._environment import get_config_dir azure_folder = get_config_dir() ACCOUNT.load(os.path.join(azure_folder, "azureProfile.json")) profile = Profile(storage=ACCOUNT) token_data = profile.get_raw_token()[0][2] return token_data except ModuleNotFoundError: try: import os import json folder = os.getenv("AZURE_CONFIG_DIR", None) or os.path.expanduser( os.path.join("~", ".azure")) token_path = os.path.join(folder, "accessTokens.json") with open(token_path) as f: data = json.load(f) # TODO: not sure I should take the first return data[0] except Exception as e: raise KustoClientError( "Azure cli token was not found. Please run 'az login' to setup account.", e)
def listDeployments(cmd, subscription=None): """Lists the deployments for the supplied subscription. If no subscription is supplied, it will use the default subscription of the current logged in user of the Azure CLI. subscription[optional]: Provides an alternate subscription to use if desired. """ if subscription is None: subscription = get_subscription_id(cmd.cli_ctx) logger.info("Using default subscription: " + subscription) profile = Profile(cli_ctx=cmd.cli_ctx) auth_token = profile.get_raw_token(subscription=subscription) creds = authentication.BasicTokenAuthentication( {'access_token': auth_token[0][1]}) ciqsapi = ciqs_api.CiqsApi(creds=creds, base_url=api.getEndpoint()) try: logger.info("Sending request.") deployments = ciqsapi.get_api_deployments_by_subscription_id( subscription) except msrest.exceptions.HttpOperationError as e: message = e.response.json() raise CLIError(message) return deployments
def _get_auth_token(self): profile = Profile(cli_ctx=self.cli_ctx) # Generate an Azure token with the VSTS resource app id auth_token, _, _ = profile.get_raw_token() content = { 'resourceId': self.remote_host, 'protocol': 'tcptunnel', 'workloadHostPort': self.remote_port, 'aztoken': auth_token[1], 'token': self.last_token, } if self.node_id: custom_header = {'X-Node-Id': self.node_id} else: custom_header = {} web_address = 'https://{}/api/tokens'.format(self.bastion.dns_name) response = requests.post( web_address, data=content, headers=custom_header, verify=(not should_disable_connection_verify())) response_json = None if response.content is not None: response_json = json.loads(response.content.decode("utf-8")) if response.status_code not in [200]: if response_json is not None and response_json[ "message"] is not None: exp = CloudError(response, error=response_json["message"]) else: exp = CloudError(response) raise exp self.last_token = response_json["authToken"] self.node_id = response_json["nodeId"] return self.last_token
def create_plan(cmd, client, resource_group_name, plan_name, location=None, tags=None, subnet_id=None, default_sku_name=None, default_autoshutdown_delay=None): import jwt # pylint: disable=import-error from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cmd.cli_ctx) creds, _, __ = profile.get_raw_token() tokenType = creds[0] accessToken = creds[1] if tokenType != "Bearer": logger.debug("Got unexpected token type: %s", tokenType) raise CLIError("Unable to create plan. Use --debug for details.") decoded_token = jwt.decode(accessToken, verify=False, algorithms=['RS256']) tid = decoded_token.get('tid') oid = decoded_token.get('oid') if not tid or not oid: logger.debug( "Unable to determine 'tid' and 'oid' from token claims: %s", decoded_token) raise CLIError("Unable to create plan. Use --debug for details.") user_id = f"{tid}_{oid}" vnet_props = VnetProperties(subnet_id=subnet_id) if subnet_id else None plan_props = VSOnlinePlanProperties( user_id=user_id, default_auto_suspend_delay_minutes=default_autoshutdown_delay, default_environment_sku=default_sku_name, vnet_properties=vnet_props) vsonline_plan = VSOnlinePlan(location=location, properties=plan_props, tags=tags) return client.create(resource_group_name, plan_name, vsonline_plan)
def get_access_token(cmd, subscription=None, resource=None, scopes=None, resource_type=None, tenant=None): """ get AAD token to access to a specified resource. Use 'az cloud show' command for other Azure resources """ if resource is None and resource_type: endpoints_attr_name = cloud_resource_type_mappings[resource_type] resource = getattr(cmd.cli_ctx.cloud.endpoints, endpoints_attr_name) profile = Profile(cli_ctx=cmd.cli_ctx) creds, subscription, tenant = profile.get_raw_token(subscription=subscription, resource=resource, scopes=scopes, tenant=tenant) result = { 'tokenType': creds[0], 'accessToken': creds[1], # 'expires_on': creds[2].get('expires_on', None), 'expiresOn': creds[2]['expiresOn'], 'tenant': tenant } if subscription: result['subscription'] = subscription return result
def send_raw_request( cli_ctx, method, url, headers=None, uri_parameters=None, # pylint: disable=too-many-locals,too-many-branches,too-many-statements body=None, skip_authorization_header=False, resource=None, output_file=None, generated_client_request_id_name='x-ms-client-request-id'): import uuid from requests import Session, Request from requests.structures import CaseInsensitiveDict result = CaseInsensitiveDict() for s in headers or []: try: temp = shell_safe_json_parse(s) result.update(temp) except CLIError: key, value = s.split('=', 1) result[key] = value headers = result # If Authorization header is already provided, don't bother with the token if 'Authorization' in headers: skip_authorization_header = True # Handle User-Agent agents = [get_az_rest_user_agent()] # Borrow AZURE_HTTP_USER_AGENT from msrest # https://github.com/Azure/msrest-for-python/blob/4cc8bc84e96036f03b34716466230fb257e27b36/msrest/pipeline/universal.py#L70 _ENV_ADDITIONAL_USER_AGENT = 'AZURE_HTTP_USER_AGENT' import os if _ENV_ADDITIONAL_USER_AGENT in os.environ: agents.append(os.environ[_ENV_ADDITIONAL_USER_AGENT]) # Custom User-Agent provided as command argument if 'User-Agent' in headers: agents.append(headers['User-Agent']) headers['User-Agent'] = ' '.join(agents) if generated_client_request_id_name: headers[generated_client_request_id_name] = str(uuid.uuid4()) # try to figure out the correct content type if body: try: _ = shell_safe_json_parse(body) if 'Content-Type' not in headers: headers['Content-Type'] = 'application/json' except Exception: # pylint: disable=broad-except pass # add telemetry headers['CommandName'] = cli_ctx.data['command'] if cli_ctx.data.get('safe_params'): headers['ParameterSetName'] = ' '.join(cli_ctx.data['safe_params']) result = {} for s in uri_parameters or []: try: temp = shell_safe_json_parse(s) result.update(temp) except CLIError: key, value = s.split('=', 1) result[key] = value uri_parameters = result or None endpoints = cli_ctx.cloud.endpoints # If url is an ARM resource ID, like /subscriptions/xxx/resourcegroups/xxx?api-version=2019-07-01, # default to Azure Resource Manager. # https://management.azure.com + /subscriptions/xxx/resourcegroups/xxx?api-version=2019-07-01 if '://' not in url: url = endpoints.resource_manager.rstrip('/') + url # Replace common tokens with real values. It is for smooth experience if users copy and paste the url from # Azure Rest API doc from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) if '{subscriptionId}' in url: url = url.replace( '{subscriptionId}', cli_ctx.data['subscription_id'] or profile.get_subscription_id()) # Prepare the Bearer token for `Authorization` header if not skip_authorization_header and url.lower().startswith('https://'): # Prepare `resource` for `get_raw_token` if not resource: # If url starts with ARM endpoint, like `https://management.azure.com/`, # use `active_directory_resource_id` for resource, like `https://management.core.windows.net/`. # This follows the same behavior as `azure.cli.core.commands.client_factory._get_mgmt_service_client` if url.lower().startswith(endpoints.resource_manager.rstrip('/')): resource = endpoints.active_directory_resource_id else: from azure.cli.core.cloud import CloudEndpointNotSetException for p in [x for x in dir(endpoints) if not x.startswith('_')]: try: value = getattr(endpoints, p) except CloudEndpointNotSetException: continue if isinstance(value, six.string_types) and url.lower().startswith( value.lower()): resource = value break if resource: # Prepare `subscription` for `get_raw_token` # If this is an ARM request, try to extract subscription ID from the URL. # But there are APIs which don't require subscription ID, like /subscriptions, /tenants # TODO: In the future when multi-tenant subscription is supported, we won't be able to uniquely identify # the token from subscription anymore. token_subscription = None if url.lower().startswith(endpoints.resource_manager.rstrip('/')): token_subscription = _extract_subscription_id(url) if token_subscription: logger.debug( 'Retrieving token for resource %s, subscription %s', resource, token_subscription) token_info, _, _ = profile.get_raw_token( resource, subscription=token_subscription) else: logger.debug('Retrieving token for resource %s', resource) token_info, _, _ = profile.get_raw_token(resource) token_type, token, _ = token_info headers = headers or {} headers['Authorization'] = '{} {}'.format(token_type, token) else: logger.warning( "Can't derive appropriate Azure AD resource from --url to acquire an access token. " "If access token is required, use --resource to specify the resource" ) # https://requests.readthedocs.io/en/latest/user/advanced/#prepared-requests s = Session() req = Request(method=method, url=url, headers=headers, params=uri_parameters, data=body) prepped = s.prepare_request(req) # Merge environment settings into session settings = s.merge_environment_settings( prepped.url, {}, None, not should_disable_connection_verify(), None) _log_request(prepped) r = s.send(prepped, **settings) _log_response(r) if not r.ok: reason = r.reason if r.text: reason += '({})'.format(r.text) raise CLIError(reason) if output_file: with open(output_file, 'wb') as fd: for chunk in r.iter_content(chunk_size=128): fd.write(chunk) return r
def _get_aad_token_after_challenge(cli_ctx, token_params, login_server, only_refresh_token, repository, artifact_repository, permission, is_diagnostics_context): authurl = urlparse(token_params['realm']) authhost = urlunparse((authurl[0], authurl[1], '/oauth2/exchange', '', '', '')) from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) # this might be a cross tenant scenario, so pass subscription to get_raw_token subscription = get_subscription_id(cli_ctx) creds, _, tenant = profile.get_raw_token(subscription=subscription) headers = {'Content-Type': 'application/x-www-form-urlencoded'} content = { 'grant_type': 'access_token', 'service': token_params['service'], 'tenant': tenant, 'access_token': creds[1] } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) if response.status_code not in [200]: from ._errors import CONNECTIVITY_REFRESH_TOKEN_ERROR if is_diagnostics_context: return CONNECTIVITY_REFRESH_TOKEN_ERROR.format_error_message(login_server, response.status_code) raise CLIError(CONNECTIVITY_REFRESH_TOKEN_ERROR.format_error_message(login_server, response.status_code) .get_error_message()) refresh_token = loads(response.content.decode("utf-8"))["refresh_token"] if only_refresh_token: return refresh_token authhost = urlunparse((authurl[0], authurl[1], '/oauth2/token', '', '', '')) if repository: scope = 'repository:{}:{}'.format(repository, permission) elif artifact_repository: scope = 'artifact-repository:{}:{}'.format(artifact_repository, permission) else: # catalog only has * as permission, even for a read operation scope = 'registry:catalog:*' content = { 'grant_type': 'refresh_token', 'service': login_server, 'scope': scope, 'refresh_token': refresh_token } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) if response.status_code not in [200]: from ._errors import CONNECTIVITY_ACCESS_TOKEN_ERROR if is_diagnostics_context: return CONNECTIVITY_ACCESS_TOKEN_ERROR.format_error_message(login_server, response.status_code) raise CLIError(CONNECTIVITY_ACCESS_TOKEN_ERROR.format_error_message(login_server, response.status_code) .get_error_message()) return loads(response.content.decode("utf-8"))["access_token"]
def _get_aad_token(cli_ctx, login_server, only_refresh_token, repository=None, artifact_repository=None, permission=None): """Obtains refresh and access tokens for an AAD-enabled registry. :param str login_server: The registry login server URL to log in to :param bool only_refresh_token: Whether to ask for only refresh token, or for both refresh and access tokens :param str repository: Repository for which the access token is requested :param str artifact_repository: Artifact repository for which the access token is requested :param str permission: The requested permission on the repository, '*' or 'pull' """ if repository and artifact_repository: raise ValueError( "Only one of repository and artifact_repository can be provided.") if (repository or artifact_repository) and permission not in ACCESS_TOKEN_PERMISSION: raise ValueError( "Permission is required for a repository or artifact_repository. Allowed access token permission: {}" .format(ACCESS_TOKEN_PERMISSION)) login_server = login_server.rstrip('/') challenge = requests.get('https://' + login_server + '/v2/', verify=(not should_disable_connection_verify())) if challenge.status_code not in [ 401 ] or 'WWW-Authenticate' not in challenge.headers: raise CLIError( "Registry '{}' did not issue a challenge.".format(login_server)) authenticate = challenge.headers['WWW-Authenticate'] tokens = authenticate.split(' ', 2) if len(tokens) < 2 or tokens[0].lower() != 'bearer': raise CLIError( "Registry '{}' does not support AAD login.".format(login_server)) params = { y[0]: y[1].strip('"') for y in (x.strip().split('=', 2) for x in tokens[1].split(',')) } if 'realm' not in params or 'service' not in params: raise CLIError( "Registry '{}' does not support AAD login.".format(login_server)) authurl = urlparse(params['realm']) authhost = urlunparse( (authurl[0], authurl[1], '/oauth2/exchange', '', '', '')) from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) creds, _, tenant = profile.get_raw_token() headers = {'Content-Type': 'application/x-www-form-urlencoded'} content = { 'grant_type': 'access_token', 'service': params['service'], 'tenant': tenant, 'access_token': creds[1] } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) if response.status_code not in [200]: raise CLIError( "Access to registry '{}' was denied. Response code: {}.".format( login_server, response.status_code)) refresh_token = loads(response.content.decode("utf-8"))["refresh_token"] if only_refresh_token: return refresh_token authhost = urlunparse( (authurl[0], authurl[1], '/oauth2/token', '', '', '')) if repository: scope = 'repository:{}:{}'.format(repository, permission) elif artifact_repository: scope = 'artifact-repository:{}:{}'.format(artifact_repository, permission) else: # catalog only has * as permission, even for a read operation scope = 'registry:catalog:*' content = { 'grant_type': 'refresh_token', 'service': login_server, 'scope': scope, 'refresh_token': refresh_token } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) if response.status_code not in [200]: raise CLIError( "Access to registry '{}' was denied. Response code: {}.".format( login_server, response.status_code)) return loads(response.content.decode("utf-8"))["access_token"]
def get_access_token(): from azure.cli.core._profile import Profile profile = Profile() creds, subscription, _ = profile.get_raw_token() return (creds[1], subscription)
def createDeployment(cmd, name, location, templateId, description=None, parameters=None, parameterFile=None, solutionStorageConnectionString=None, subscription=None): """Creates a deployment with the specified parameters. name: The name of the deployment. (must be alphanumeric lowercase between 3 to 9 characters beginning with a letter) location: The location to deploy the solution to. templateId: The unique id of the template which the deployment will be bulit from. description[optional]: The optional description of the deployment. parameters[optional]: solutionStorageConnectionString[optional]: subscription[optional]: Provides an alternate subscription to use if desired. """ if subscription is None: subscription = get_subscription_id(cmd.cli_ctx) logger.info("Using default subscription: " + subscription) profile = Profile(cli_ctx=cmd.cli_ctx) auth_token = profile.get_raw_token(subscription=subscription) if parameters is not None and parameterFile is not None: raise CLIError( "May not have parameters and a parameters file at the same time.") elif parameterFile is not None: logger.info("Using parameter file: " + parameterFile) try: with open(parameterFile) as jsonfile: parameters = json.load(jsonfile) logger.info("Parameters loaded.") except IOError: raise CLIError("Could not open file.") elif parameters is not None: logger.info("Using parameters from commandline argument.") validators.validate_json_arg(parameters) parameters = json.loads(parameters) logger.info("Parameters loaded") creds = authentication.BasicTokenAuthentication( {'access_token': auth_token[0][1]}) ciqsapi = ciqs_api.CiqsApi(creds=creds, base_url=api.getEndpoint()) request = models.MicrosoftCiqsModelsDeploymentCreateDeploymentRequest( name, location, template_id=templateId, subscription=subscription, description=description, referrer='az ciqs', solution_storage_connection_string=solutionStorageConnectionString, parameters=parameters) try: logger.info("Sending request.") response = ciqsapi.post_api_deployments_by_subscription_id_by_template_id( subscription_id=subscription, template_id=templateId, body=request, ms_asm_refresh_token=auth_token[0][2]['refreshToken']) except msrest.exceptions.HttpOperationError as e: message = e.response.json() raise CLIError(message) return response
def _get_aad_token(cli_ctx, login_server, only_refresh_token, repository=None, artifact_repository=None, permission=None): """Obtains refresh and access tokens for an AAD-enabled registry. :param str login_server: The registry login server URL to log in to :param bool only_refresh_token: Whether to ask for only refresh token, or for both refresh and access tokens :param str repository: Repository for which the access token is requested :param str artifact_repository: Artifact repository for which the access token is requested :param str permission: The requested permission on the repository, '*' or 'pull' """ if repository and artifact_repository: raise ValueError("Only one of repository and artifact_repository can be provided.") if (repository or artifact_repository) and permission not in ACCESS_TOKEN_PERMISSION: raise ValueError( "Permission is required for a repository or artifact_repository. Allowed access token permission: {}" .format(ACCESS_TOKEN_PERMISSION)) login_server = login_server.rstrip('/') challenge = requests.get('https://' + login_server + '/v2/', verify=(not should_disable_connection_verify())) if challenge.status_code not in [401] or 'WWW-Authenticate' not in challenge.headers: raise CLIError("Registry '{}' did not issue a challenge.".format(login_server)) authenticate = challenge.headers['WWW-Authenticate'] tokens = authenticate.split(' ', 2) if len(tokens) < 2 or tokens[0].lower() != 'bearer': raise CLIError("Registry '{}' does not support AAD login.".format(login_server)) params = {y[0]: y[1].strip('"') for y in (x.strip().split('=', 2) for x in tokens[1].split(','))} if 'realm' not in params or 'service' not in params: raise CLIError("Registry '{}' does not support AAD login.".format(login_server)) authurl = urlparse(params['realm']) authhost = urlunparse((authurl[0], authurl[1], '/oauth2/exchange', '', '', '')) from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) creds, _, tenant = profile.get_raw_token() headers = {'Content-Type': 'application/x-www-form-urlencoded'} content = { 'grant_type': 'access_token', 'service': params['service'], 'tenant': tenant, 'access_token': creds[1] } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) if response.status_code not in [200]: raise CLIError("Access to registry '{}' was denied. Response code: {}.".format( login_server, response.status_code)) refresh_token = loads(response.content.decode("utf-8"))["refresh_token"] if only_refresh_token: return refresh_token authhost = urlunparse((authurl[0], authurl[1], '/oauth2/token', '', '', '')) if repository: scope = 'repository:{}:{}'.format(repository, permission) elif artifact_repository: scope = 'artifact-repository:{}:{}'.format(artifact_repository, permission) else: # catalog only has * as permission, even for a read operation scope = 'registry:catalog:*' content = { 'grant_type': 'refresh_token', 'service': login_server, 'scope': scope, 'refresh_token': refresh_token } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) if response.status_code not in [200]: raise CLIError("Access to registry '{}' was denied. Response code: {}.".format( login_server, response.status_code)) return loads(response.content.decode("utf-8"))["access_token"]
def send_raw_request( cli_ctx, method, uri, headers=None, uri_parameters=None, # pylint: disable=too-many-locals,too-many-branches,too-many-statements body=None, skip_authorization_header=False, resource=None, output_file=None, generated_client_request_id_name='x-ms-client-request-id'): import uuid import requests from azure.cli.core.commands.client_factory import UA_AGENT result = {} for s in headers or []: try: temp = shell_safe_json_parse(s) result.update(temp) except CLIError: key, value = s.split('=', 1) result[key] = value headers = result headers.update({ 'User-Agent': UA_AGENT, }) if generated_client_request_id_name: headers[generated_client_request_id_name] = str(uuid.uuid4()) # try to figure out the correct content type if body: try: _ = shell_safe_json_parse(body) if 'Content-Type' not in headers: headers['Content-Type'] = 'application/json' except Exception: # pylint: disable=broad-except pass # add telemetry headers['CommandName'] = cli_ctx.data['command'] if cli_ctx.data.get('safe_params'): headers['ParameterSetName'] = ' '.join(cli_ctx.data['safe_params']) result = {} for s in uri_parameters or []: try: temp = shell_safe_json_parse(s) result.update(temp) except CLIError: key, value = s.split('=', 1) result[key] = value uri_parameters = result or None if '://' not in uri: uri = cli_ctx.cloud.endpoints.resource_manager + uri.lstrip('/') # Replace common tokens with real values. It is for smooth experience if users copy and paste the url from # Azure Rest API doc from azure.cli.core._profile import Profile profile = Profile() if '{subscriptionId}' in uri: uri = uri.replace('{subscriptionId}', profile.get_subscription_id()) if not skip_authorization_header and uri.lower().startswith('https://'): if not resource: endpoints = cli_ctx.cloud.endpoints from azure.cli.core.cloud import CloudEndpointNotSetException for p in [x for x in dir(endpoints) if not x.startswith('_')]: try: value = getattr(endpoints, p) except CloudEndpointNotSetException: continue if isinstance(value, six.string_types) and uri.lower().startswith( value.lower()): resource = value break if resource: token_info, _, _ = profile.get_raw_token(resource) logger.debug('Retrievd AAD token for resource: %s', resource or 'ARM') token_type, token, _ = token_info headers = headers or {} headers['Authorization'] = '{} {}'.format(token_type, token) else: logger.warning( "Can't derive appropriate Azure AD resource from --url to acquire an access token. " "If access token is required, use --resource to specify the resource" ) try: r = requests.request(method, uri, params=uri_parameters, data=body, headers=headers, verify=not should_disable_connection_verify()) logger.debug("Response Header : %s", r.headers if r else '') except Exception as ex: # pylint: disable=broad-except raise CLIError(ex) if not r.ok: reason = r.reason if r.text: reason += '({})'.format(r.text) raise CLIError(reason) if output_file: with open(output_file, 'wb') as fd: for chunk in r.iter_content(chunk_size=128): fd.write(chunk) return r
class MediaV2Client(): """ Media V2 Client """ def __init__(self, cli_ctx, resource_group_name, account_name): from azure.cli.core._profile import Profile self.profile = Profile(cli_ctx=cli_ctx) self._old_rp_api_version = '2015-10-01' self.v2_media_api_resource = cli_ctx.cloud.endpoints.media_resource_id self.api_endpoint = self._get_v2_api_endpoint(cli_ctx, resource_group_name, account_name) self.access_token = self._get_v2_access_token() def _get_v2_api_endpoint(self, cli_ctx, resource_group_name, account_name): from msrestazure.tools import resource_id from azure.cli.command_modules.ams._sdk_utils import (get_media_namespace, get_media_type) access_token = self.profile.get_raw_token()[0][2].get('accessToken') media_old_rp_url = resource_id(subscription=get_subscription_id(cli_ctx), resource_group=resource_group_name, namespace=get_media_namespace(), type=get_media_type(), name=account_name) + '?api-version={}'.format(self._old_rp_api_version) media_service_res = requests.get(cli_ctx.cloud.endpoints.resource_manager.rstrip('/') + media_old_rp_url, headers={'Authorization': 'Bearer {}'.format(access_token)}) if not media_service_res.ok: err_info = 'Request to 2015-10-01 Media API failed.' res_text = json.loads(media_service_res.text) if res_text is not None and res_text.get('error') is not None: err_info = res_text.get('error').get('message') raise CLIError(err_info) media_service = media_service_res.json() api_endpoints = media_service.get('properties').get('apiEndpoints') api_endpoint = next((x for x in api_endpoints if x.get('majorVersion') == '2'), api_endpoints[0]) if not api_endpoint: raise CLIError('v2 Media API endpoint was not found.') return api_endpoint def _get_v2_access_token(self): # pylint: disable=protected-access access_token = self.profile.get_raw_token(resource=self.v2_media_api_resource)[0][2].get('accessToken') return access_token def set_mru(self, account_id, count, type): headers = {} headers['Authorization'] = 'Bearer {}'.format(self.access_token) headers['Content-Type'] = 'application/json;odata=verbose' headers['Accept'] = 'application/json;odata=verbose' url_request_template = "{}EncodingReservedUnitTypes(guid'{}')?api-version=2.19" response = requests.put(url_request_template.format(self.api_endpoint.get('endpoint'), account_id), headers=headers, data='{{"ReservedUnitType":{}, "CurrentReservedUnits":{}}}'.format(type, count)) if not response.ok: err_info = 'No error information available' if json.loads(response.text) is not None and json.loads(response.text).get('error') is not None: err_info = json.loads(response.text).get('error').get('message').get('value') raise CLIError('Request to EncodingReservedUnitTypes v2 API endpoint failed. ' + err_info) def get_mru(self): headers = {} headers['Authorization'] = 'Bearer {}'.format(self.access_token) headers['Content-Type'] = 'application/json;odata=minimalmetadata' headers['Accept'] = 'application/json;odata=minimalmetadata' headers['Accept-Charset'] = 'UTF-8' url_request_template = '{}EncodingReservedUnitTypes?api-version=2.19' response = requests.get(url_request_template.format(self.api_endpoint.get('endpoint')), headers=headers) if not response.ok: raise CLIError('Request to EncodingReservedUnitTypes v2 API endpoint failed.') return response.json().get('value')[0]
def token_provider(): from azure.cli.core._profile import Profile profile = Profile(cli_ctx=target["cmd"].cli_ctx) creds, _, _ = profile.get_raw_token(resource=IOTHUB_RESOURCE_ID) access_token = AccessToken(f"{creds[0]} {creds[1]}", time() + 3599) return access_token