def test_client_id_none_app_service_2017_09_01(): """The credential should ignore client_id=None. App Service 2017-09-01 must be tested separately due to its eccentric expires_on format. """ expected_access_token = "****" scope = "scope" def send(request, **_): assert "client_id" not in request.query assert "clientid" not in request.query return mock_response( json_payload=( build_aad_response( access_token=expected_access_token, expires_on="01/01/1970 00:00:42 +00:00", resource=scope ) ) ) with mock.patch.dict( MANAGED_IDENTITY_ENVIRON, {EnvironmentVariables.MSI_ENDPOINT: "https://localhost", EnvironmentVariables.MSI_SECRET: "secret"}, clear=True, ): credential = ManagedIdentityCredential(client_id=None, transport=mock.Mock(send=send)) token = credential.get_token(scope) assert token.token == expected_access_token
def get_credentials_from_connection_infoV2(connection_infos): infos = connection_infos user_managed_identity = infos.get('userManagedIdentity', None) identity_type = infos.get('identityType', 'default') managed_identity_id = None if identity_type == 'default': credentials = DefaultAzureCredential() elif identity_type == 'user-assigned': managed_identity_id = infos.get('userManagedIdentityId') if managed_identity_id.startswith("/"): credentials = ManagedIdentityCredential( identity_config={'msi_res_id': managed_identity_id}) else: credentials = ManagedIdentityCredential( client_id=managed_identity_id) elif identity_type == 'service-principal': client_id = infos.get('clientId', None) password = infos.get('password', None) tenant_id = infos.get('tenantId', None) credentials = ClientSecretCredential(tenant_id, client_id, password) else: raise Exception( "Identity type {} is unknown and cannot be used".format( identity_type)) return credentials, managed_identity_id
def test_app_service_2017_09_01(): """When the environment for 2019-08-01 is not configured, 2017-09-01 should be used.""" access_token = "****" expires_on = 42 expected_token = AccessToken(access_token, expires_on) url = "http://localhost:42/token" secret = "expected-secret" scope = "scope" transport = validating_transport( requests=[ Request( url, method="GET", required_headers={ "secret": secret, "User-Agent": USER_AGENT }, required_params={ "api-version": "2017-09-01", "resource": scope }, ) ] * 2, responses=[ mock_response( json_payload={ "access_token": access_token, "expires_on": "01/01/1970 00:00:{} +00:00".format( expires_on), # linux format "resource": scope, "token_type": "Bearer", }), mock_response( json_payload={ "access_token": access_token, "expires_on": "1/1/1970 12:00:{} AM +00:00".format( expires_on), # windows format "resource": scope, "token_type": "Bearer", }), ], ) with mock.patch.dict( MANAGED_IDENTITY_ENVIRON, { EnvironmentVariables.MSI_ENDPOINT: url, EnvironmentVariables.MSI_SECRET: secret, }, clear=True, ): token = ManagedIdentityCredential(transport=transport).get_token(scope) assert token == expected_token assert token.expires_on == expires_on token = ManagedIdentityCredential(transport=transport).get_token(scope) assert token == expected_token assert token.expires_on == expires_on
def _init_impl(self): try: self.msi_auth_context = ManagedIdentityCredential(**self._msi_args) except Exception as e: raise KustoClientError( "Failed to initialize MSI ManagedIdentityCredential with [" + str(self._msi_params) + "]\n" + str(e))
def test_app_service_user_assigned_identity(): """App Service 2017-09-01: MSI_ENDPOINT, MSI_SECRET set""" expected_token = "****" expires_on = 42 client_id = "some-guid" endpoint = "http://localhost:42/token" secret = "expected-secret" scope = "scope" param_name, param_value = "foo", "bar" transport = validating_transport( requests=[ Request( base_url=endpoint, method="GET", required_headers={"secret": secret, "User-Agent": USER_AGENT}, required_params={"api-version": "2017-09-01", "clientid": client_id, "resource": scope}, ), Request( base_url=endpoint, method="GET", required_headers={"secret": secret, "User-Agent": USER_AGENT}, required_params={ "api-version": "2017-09-01", "clientid": client_id, "resource": scope, param_name: param_value, }, ), ], responses=[ mock_response( json_payload={ "access_token": expected_token, "expires_on": "01/01/1970 00:00:{} +00:00".format(expires_on), "resource": scope, "token_type": "Bearer", } ) ] * 2, ) with mock.patch.dict( MANAGED_IDENTITY_ENVIRON, {EnvironmentVariables.MSI_ENDPOINT: endpoint, EnvironmentVariables.MSI_SECRET: secret}, clear=True, ): token = ManagedIdentityCredential(client_id=client_id, transport=transport).get_token(scope) assert token.token == expected_token assert token.expires_on == expires_on credential = ManagedIdentityCredential( client_id=client_id, transport=transport, identity_config={param_name: param_value} ) token = credential.get_token(scope) assert token.token == expected_token assert token.expires_on == expires_on
def test_close(environ): transport = mock.MagicMock() with mock.patch.dict("os.environ", environ, clear=True): credential = ManagedIdentityCredential(transport=transport) assert transport.__exit__.call_count == 0 credential.close() assert transport.__exit__.call_count == 1
def _get_token_impl(self) -> dict: try: if self._msi_auth_context is None: self._msi_auth_context = ManagedIdentityCredential(**self._msi_args) msi_token = self._msi_auth_context.get_token(self._kusto_uri) return {TokenConstants.MSAL_TOKEN_TYPE: TokenConstants.BEARER_TYPE, TokenConstants.MSAL_ACCESS_TOKEN: msi_token.token} except ClientAuthenticationError as e: raise KustoClientError("Failed to initialize MSI ManagedIdentityCredential with [{0}]\n{1}".format(self._msi_args, e)) except Exception as e: raise KustoClientError("Failed to obtain MSI token for '{0}' with [{1}]\n{2}".format(self._kusto_uri, self._msi_args, e))
def get_azure_credentials(config): credentials = None cloud_environment = get_azure_cloud_environment(config) if config.UseMSI and cloud_environment: try: from azure.identity import ManagedIdentityCredential credentials = ManagedIdentityCredential( cloud_environment=cloud_environment) except ImportError: from msrestazure.azure_active_directory import MSIAuthentication credentials = MSIAuthentication( cloud_environment=cloud_environment) elif config.UseMSI: try: from azure.identity import ManagedIdentityCredential credentials = ManagedIdentityCredential() except ImportError: from msrestazure.azure_active_directory import MSIAuthentication credentials = MSIAuthentication() elif cloud_environment: try: # try to use new libraries ClientSecretCredential (azure.identity, based on azure.core) from azure.identity import ClientSecretCredential credentials = ClientSecretCredential( client_id=config.ApplicationId, client_secret=config.ApplicationKey, tenant_id=config.Tenantid, cloud_environment=cloud_environment) except ImportError: # use old libraries ServicePrincipalCredentials (azure.common) if new one is not available from azure.common.credentials import ServicePrincipalCredentials credentials = ServicePrincipalCredentials( client_id=config.ApplicationId, secret=config.ApplicationKey, tenant=config.Tenantid, cloud_environment=cloud_environment) else: try: # try to use new libraries ClientSecretCredential (azure.identity, based on azure.core) from azure.identity import ClientSecretCredential credentials = ClientSecretCredential( client_id=config.ApplicationId, client_secret=config.ApplicationKey, tenant_id=config.Tenantid) except ImportError: # use old libraries ServicePrincipalCredentials (azure.common) if new one is not available from azure.common.credentials import ServicePrincipalCredentials credentials = ServicePrincipalCredentials( client_id=config.ApplicationId, secret=config.ApplicationKey, tenant=config.Tenantid) return credentials
def test_azure_arc_client_id(): """Azure Arc doesn't support user-assigned managed identity""" with mock.patch( "os.environ", { EnvironmentVariables.IDENTITY_ENDPOINT: "http://localhost:42/token", EnvironmentVariables.IMDS_ENDPOINT: "http://localhost:42", }): credential = ManagedIdentityCredential(client_id="some-guid") with pytest.raises(ClientAuthenticationError): credential.get_token("scope")
def get_token_from_msi(self) -> AccessToken: try: if self.msi_auth_context is None: # Create the MSI Authentication object self.msi_auth_context = ManagedIdentityCredential( **self.msi_params) return self.msi_auth_context.get_token(self.kusto_uri) except Exception as e: raise KustoClientError("Failed to obtain MSI context for [" + str(self.msi_params) + "]\n" + str(e))
def test_azure_ml(): """Azure ML: MSI_ENDPOINT, MSI_SECRET set (like App Service 2017-09-01 but with a different response format)""" expected_token = AccessToken("****", int(time.time()) + 3600) url = "http://localhost:42/token" secret = "expected-secret" scope = "scope" client_id = "client" transport = validating_transport( requests=[ Request( url, method="GET", required_headers={"secret": secret, "User-Agent": USER_AGENT}, required_params={"api-version": "2017-09-01", "resource": scope}, ), Request( url, method="GET", required_headers={"secret": secret, "User-Agent": USER_AGENT}, required_params={"api-version": "2017-09-01", "resource": scope, "clientid": client_id}, ), ], responses=[ mock_response( json_payload={ "access_token": expected_token.token, "expires_in": 3600, "expires_on": expected_token.expires_on, "resource": scope, "token_type": "Bearer", } ) ] * 2, ) with mock.patch.dict( MANAGED_IDENTITY_ENVIRON, {EnvironmentVariables.MSI_ENDPOINT: url, EnvironmentVariables.MSI_SECRET: secret}, clear=True, ): token = ManagedIdentityCredential(transport=transport).get_token(scope) assert token.token == expected_token.token assert token.expires_on == expected_token.expires_on token = ManagedIdentityCredential(transport=transport, client_id=client_id).get_token(scope) assert token.token == expected_token.token assert token.expires_on == expected_token.expires_on
def test_cloud_shell_user_assigned_identity(): """Cloud Shell environment: only MSI_ENDPOINT set""" expected_token = "****" expires_on = 42 client_id = "some-guid" endpoint = "http://localhost:42/token" scope = "scope" param_name, param_value = "foo", "bar" transport = validating_transport( requests=[ Request( base_url=endpoint, method="POST", required_headers={"Metadata": "true", "User-Agent": USER_AGENT}, required_data={"client_id": client_id, "resource": scope}, ), Request( base_url=endpoint, method="POST", required_headers={"Metadata": "true", "User-Agent": USER_AGENT}, required_data={"resource": scope, param_name: param_value}, ), ], responses=[ mock_response( json_payload={ "access_token": expected_token, "expires_in": 0, "expires_on": expires_on, "not_before": int(time.time()), "resource": scope, "token_type": "Bearer", } ) ] * 2, ) with mock.patch.dict(MANAGED_IDENTITY_ENVIRON, {EnvironmentVariables.MSI_ENDPOINT: endpoint}, clear=True): token = ManagedIdentityCredential(client_id=client_id, transport=transport).get_token(scope) assert token.token == expected_token assert token.expires_on == expires_on credential = ManagedIdentityCredential(transport=transport, identity_config={param_name: param_value}) token = credential.get_token(scope) assert token.token == expected_token assert token.expires_on == expires_on
def _get_credential(self): if os.getenv("USE_MSI", "false").lower() == "true": _logger.info('Using MSI') if "MSI_CLIENT_ID" in os.environ: msi_client_id = os.environ["MSI_CLIENT_ID"] _logger.info('Using client_id: %s', msi_client_id) credentials = ManagedIdentityCredential( client_id=msi_client_id) elif "MSI_OBJECT_ID" in os.environ: msi_object_id = os.environ["MSI_OBJECT_ID"] identity = {'object_id': msi_object_id} _logger.info('Using object_id: %s', msi_object_id) credentials = ManagedIdentityCredential( identity_config=identity) elif "MSI_RESOURCE_ID" in os.environ: msi_resource_id = os.environ["MSI_RESOURCE_ID"] identity = {'resource_id': msi_resource_id} _logger.info('Using resource_id: %s', msi_resource_id) credentials = ManagedIdentityCredential( identity_config=identity) else: credentials = ManagedIdentityCredential() else: if os.getenv("USE_ENV", "false").lower() == "true": self._parse_sp_env() else: self._parse_sp_file() # azure.json file will have "msi" as the client_id and client_secret # if the node is running managed identity if self.client_id == "msi" and self.client_secret == "msi": _logger.info('Using MSI') # refer _parse_sp_file, potentially we could have mi client id from sp if self.user_assigned_identity_id != "": _logger.info('Using client_id: %s', self.user_assigned_identity_id) credentials = ManagedIdentityCredential( client_id=self.user_assigned_identity_id) else: credentials = DefaultAzureCredential() else: _logger.info('Using ClientSecretCredential') principal = { 'tenant_id': self.tenant_id, 'client_id': self.client_id, 'client_secret': self.client_secret, } credentials = ClientSecretCredential( **principal, authority=AZURE_AUTHORITY_SERVER) return credentials
def test_token_exchange_tenant_id(tmpdir): exchange_token = "exchange-token" token_file = tmpdir.join("token") token_file.write(exchange_token) access_token = "***" authority = "https://localhost" default_client_id = "default_client_id" tenant = "tenant_id" scope = "scope" success_response = mock_response( json_payload={ "access_token": access_token, "expires_in": 3600, "ext_expires_in": 3600, "expires_on": int(time.time()) + 3600, "not_before": int(time.time()), "resource": scope, "token_type": "Bearer", }) transport = validating_transport( requests=[ Request( base_url=authority, method="POST", required_data={ "client_assertion": exchange_token, "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", "client_id": default_client_id, "grant_type": "client_credentials", "scope": scope, }, ) ], responses=[success_response], ) mock_environ = { EnvironmentVariables.AZURE_AUTHORITY_HOST: authority, EnvironmentVariables.AZURE_CLIENT_ID: default_client_id, EnvironmentVariables.AZURE_TENANT_ID: tenant, EnvironmentVariables.AZURE_FEDERATED_TOKEN_FILE: token_file.strpath, } with mock.patch.dict("os.environ", mock_environ, clear=True): credential = ManagedIdentityCredential(transport=transport) token = credential.get_token(scope, tenant_id="tenant_id") assert token.token == access_token
def test_app_service(): """App Service environment: MSI_ENDPOINT, MSI_SECRET set""" access_token = "****" expires_on = 42 expected_token = AccessToken(access_token, expires_on) url = "http://localhost:42/token" secret = "expected-secret" scope = "scope" transport = validating_transport( requests=[ Request( url, method="GET", required_headers={"Metadata": "true", "secret": secret}, required_params={"api-version": "2017-09-01", "resource": scope}, ) ], responses=[ mock_response( json_payload={ "access_token": access_token, "expires_on": expires_on, "resource": scope, "token_type": "Bearer", } ) ], ) with mock.patch("os.environ", {EnvironmentVariables.MSI_ENDPOINT: url, EnvironmentVariables.MSI_SECRET: secret}): token = ManagedIdentityCredential(transport=transport).get_token(scope) assert token == expected_token
def test_cloud_shell_user_assigned_identity(): """Cloud Shell environment: only MSI_ENDPOINT set""" access_token = "****" expires_on = 42 client_id = "some-guid" expected_token = AccessToken(access_token, expires_on) endpoint = "http://localhost:42/token" scope = "scope" transport = validating_transport( requests=[ Request( base_url=endpoint, method="POST", required_headers={"Metadata": "true", "User-Agent": USER_AGENT}, required_data={"client_id": client_id, "resource": scope}, ) ], responses=[ mock_response( json_payload={ "access_token": access_token, "expires_in": 0, "expires_on": expires_on, "not_before": int(time.time()), "resource": scope, "token_type": "Bearer", } ) ], ) with mock.patch("os.environ", {EnvironmentVariables.MSI_ENDPOINT: endpoint}): token = ManagedIdentityCredential(client_id=client_id, transport=transport).get_token(scope) assert token == expected_token
def test_cloud_shell(): """Cloud Shell environment: only MSI_ENDPOINT set""" access_token = "****" expires_on = 42 expected_token = AccessToken(access_token, expires_on) url = "http://localhost:42/token" scope = "scope" transport = validating_transport( requests=[ Request(url, method="POST", required_headers={"Metadata": "true"}, required_data={"resource": scope}) ], responses=[ mock_response( json_payload={ "access_token": access_token, "expires_in": 0, "expires_on": expires_on, "not_before": int(time.time()), "resource": scope, "token_type": "Bearer", }) ], ) with mock.patch("os.environ", {EnvironmentVariables.MSI_ENDPOINT: url}): token = ManagedIdentityCredential(transport=transport).get_token(scope) assert token == expected_token
def test_app_service_user_assigned_identity(): """App Service environment: MSI_ENDPOINT, MSI_SECRET set""" expected_secret = "expected-secret" client_id = "some-guid" access_token = "AccessToken" url = "http://localhost:42/token" token_json = json.dumps({ "access_token": access_token, "expires_on": int(time.time() + 3600) }) httpretty.register_uri(httpretty.GET, url, body=token_json, content_type="application/json") with mock.patch( "os.environ", { EnvironmentVariables.MSI_ENDPOINT: url, EnvironmentVariables.MSI_SECRET: expected_secret }): token = ManagedIdentityCredential( client_id=client_id).get_token("scope") assert token.token == access_token assert len(httpretty.httpretty.latest_requests) == 1 last_request = httpretty.last_request() assert last_request.headers["secret"] == expected_secret assert last_request.querystring.get("client_id") == [client_id] # pylint:disable=no-member
def test_imds(): access_token = "****" expires_on = 42 expected_token = AccessToken(access_token, expires_on) url = Endpoints.IMDS scope = "scope" transport = validating_transport( requests=[ Request(url), # first request should be availability probe => match only the URL Request( url, method="GET", required_headers={"Metadata": "true"}, required_params={"api-version": "2018-02-01", "resource": scope}, ), ], responses=[ # probe receives error response mock_response(status_code=400, json_payload={"error": "this is an error message"}), mock_response( json_payload={ "access_token": access_token, "expires_in": 42, "expires_on": expires_on, "ext_expires_in": 42, "not_before": int(time.time()), "resource": scope, "token_type": "Bearer", } ), ], ) token = ManagedIdentityCredential(transport=transport).get_token(scope) assert token == expected_token
def test_imds_user_assigned_identity(): access_token = "AccessToken" token_json = json.dumps({ "access_token": access_token, "expires_on": int(time.time() + 3600) }) scope = "scope" client_id = "client-id" class reqs: count = 0 def request_callback(request, uri, response_headers): reqs.count += 1 if reqs.count == 1: # credential is probing to determine endpoint availability return 400, response_headers, json.dumps({}) assert request.headers["Metadata"] == "true" assert request.querystring.get("client_id") == [client_id] assert request.querystring.get("resource") == [scope] return 200, response_headers, token_json httpretty.register_uri(httpretty.GET, Endpoints.IMDS, body=request_callback, content_type="application/json") token = ManagedIdentityCredential(client_id=client_id).get_token(scope) assert token.token == access_token assert len(httpretty.httpretty.latest_requests) == 2
def test_app_service(): """App Service environment: MSI_ENDPOINT, MSI_SECRET set""" expected_secret = "expected-secret" access_token = "AccessToken" url = "http://localhost:42/token" token_json = json.dumps({ "access_token": access_token, "expires_on": int(time.time() + 3600) }) httpretty.register_uri(httpretty.GET, url, body=token_json, content_type="application/json") with mock.patch( "os.environ", { EnvironmentVariables.MSI_ENDPOINT: url, EnvironmentVariables.MSI_SECRET: expected_secret }): token = ManagedIdentityCredential().get_token("scope") assert token.token == access_token assert httpretty.last_request().headers["secret"] == expected_secret assert len(httpretty.httpretty.latest_requests) == 1
def test_service_fabric_tenant_id(): access_token = "****" expires_on = 42 endpoint = "http://localhost:42/token" secret = "expected-secret" thumbprint = "SHA1HEX" scope = "scope" def send(request, **_): assert request.url.startswith(endpoint) assert request.method == "GET" assert request.headers["Secret"] == secret assert request.query["api-version"] == "2019-07-01-preview" assert request.query["resource"] == scope return mock_response( json_payload={ "access_token": access_token, "expires_on": str(expires_on), "resource": scope, "token_type": "Bearer", }) with mock.patch( "os.environ", { EnvironmentVariables.IDENTITY_ENDPOINT: endpoint, EnvironmentVariables.IDENTITY_HEADER: secret, EnvironmentVariables.IDENTITY_SERVER_THUMBPRINT: thumbprint, }, ): token = ManagedIdentityCredential(transport=mock.Mock( send=send)).get_token(scope, tenant_id="tenant_id") assert token.token == access_token assert token.expires_on == expires_on
class MsiTokenProvider(TokenProviderBase): """ MSI Token Provider obtains a token from the MSI endpoint The args parameter is a dictionary conforming with the ManagedIdentityCredential initializer API arguments """ def __init__(self, kusto_uri: str, msi_args): super().__init__(kusto_uri) self._msi_args = msi_args self._msi_auth_context = None @staticmethod def name() -> str: return "MsiTokenProvider" def context(self) -> dict: context = self._msi_args.copy() context["authority"] = self.name() return context def _init_impl(self): try: self.msi_auth_context = ManagedIdentityCredential(**self._msi_args) except Exception as e: raise KustoClientError("Failed to initialize MSI ManagedIdentityCredential with [" + str(self._msi_params) + "]\n" + str(e)) def _get_token_impl(self) -> dict: return None def _get_token_from_cache_impl(self) -> dict: try: msi_token = self.msi_auth_context.get_token(self._kusto_uri) return {TokenConstants.MSAL_TOKEN_TYPE: TokenConstants.BEARER_TYPE, TokenConstants.MSAL_ACCESS_TOKEN: msi_token.token} except Exception as e: raise KustoClientError("Failed to obtain MSI token for '" + self._kusto_uri + "' with [" + str(self._msi_params) + "]\n" + str(e))
def _default_chained_credentials(self) -> ChainedTokenCredential: managed_identity = ManagedIdentityCredential() azure_cli = AzureCliCredential() environment = EnvironmentCredential() shared_token_cache = SharedTokenCacheCredential() interactive_browser = InteractiveBrowserCredential() return ChainedTokenCredential(managed_identity, azure_cli, environment, shared_token_cache, interactive_browser)
def get_azure_token(): credential_chain = ChainedTokenCredential(ManagedIdentityCredential(), AzureCliCredential()) try: token = credential_chain.get_token("https://monitoring.azure.com//.default") return token.token except Exception as e: logging.exception(f"Failed to retrieve Azure token. Reason is {type(e).__name__} {e}") return None
def get_secret_from_vault(self): # Connect to vault credential = ManagedIdentityCredential() secret_client = SecretClient(vault_url=self.vault_url, credential=credential) return secret_client.get_secret(self.secret_name)
def __init__(self, tracer: logging.Logger, sapmonId: str, msiClientId: str, subscriptionId: str, resourceGroup: str): self.tracer = tracer self.tracer.info("initializing Storage Account instance") self.accountName = STORAGE_ACCOUNT_NAMING_CONVENTION % sapmonId self.token = ManagedIdentityCredential(client_id=msiClientId) self.subscriptionId = subscriptionId self.resourceGroup = resourceGroup
def __init__(self, tracer: logging.Logger, kvName: str, msiClientId: Optional[str] = None): self.tracer = tracer self.tracer.info("initializing KeyVault %s" % kvName) self.kvName = kvName self.uri = "https://%s.vault.azure.net" % kvName self.token = ManagedIdentityCredential(client_id = msiClientId) self.kv_client = SecretClient(vault_url=self.uri, credential = self.token)
def test_cloud_shell_live(cloud_shell): credential = ManagedIdentityCredential() token = credential.get_token("https://vault.azure.net") # Validate the token by sending a request to the Key Vault. The request is manual because azure-keyvault-secrets # can't authenticate in Cloud Shell; the MSI endpoint there doesn't support AADv2 scopes. policies = [ ContentDecodePolicy(), RedirectPolicy(), RetryPolicy(), HttpLoggingPolicy() ] client = PipelineClient(cloud_shell["vault_url"], policies=policies) list_secrets = client.get( "secrets", headers={"Authorization": "Bearer " + token.token}, params={"api-version": "7.0"}) with client: client._pipeline.run(list_secrets)
def test_managed_identity_live(live_managed_identity_config): credential = ManagedIdentityCredential( client_id=live_managed_identity_config["client_id"]) # do something with Key Vault to verify the credential can get a valid token client = SecretClient(live_managed_identity_config["vault_url"], credential, logging_enable=True) for _ in client.list_properties_of_secrets(): pass