def get_azure_credentials(config): credentials = None cloud_environment = get_azure_cloud_environment(config) if config.UseMSI and cloud_environment: from msrestazure.azure_active_directory import MSIAuthentication credentials = MSIAuthentication(cloud_environment=cloud_environment) elif config.UseMSI: from msrestazure.azure_active_directory import MSIAuthentication credentials = MSIAuthentication() elif cloud_environment: from azure.identity import ClientSecretCredential credentials = ClientSecretCredential( client_id = config.ApplicationId, client_secret = config.ApplicationKey, tenant_id = config.Tenantid, cloud_environment=cloud_environment ) else: from azure.identity import ClientSecretCredential credentials = ClientSecretCredential( client_id = config.ApplicationId, client_secret = config.ApplicationKey, tenant_id = config.Tenantid ) return credentials
class MemoryCacheRead(PerfStressTest): def __init__(self, arguments): super().__init__(arguments) client_id = self.get_from_env("AZURE_CLIENT_ID") tenant_id = self.get_from_env("AZURE_TENANT_ID") secret = self.get_from_env("AZURE_CLIENT_SECRET") self.credential = ClientSecretCredential(tenant_id, client_id, secret) self.async_credential = AsyncClientSecretCredential( tenant_id, client_id, secret) self.scope = "https://vault.azure.net/.default" async def global_setup(self): """Cache an access token""" await super().global_setup() self.credential.get_token(self.scope) await self.async_credential.get_token(self.scope) def run_sync(self): self.credential.get_token(self.scope) async def run_async(self): await self.async_credential.get_token(self.scope) async def close(self): await self.async_credential.close() await super().close()
def __init__(self): if os.environ.get('IS_PROD', None): self.credential = ClientSecretCredential( tenant_id=os.environ.get('tenant_id', None), client_id=os.environ.get('client_id', None), client_secret=os.environ.get('client_secret', None), ) self.secret_client = SecretClient(vault_url=os.environ.get( 'vault_url', None), credential=self.credential) self.key_client = KeyClient(vault_url=os.environ.get( 'vault_url', None), credential=self.credential) else: self.credential = ClientSecretCredential( tenant_id=Configuration.tenant_id, client_id=Configuration.client_id, client_secret=Configuration.client_secret, ) self.secret_client = SecretClient( vault_url=Configuration.vault_url, credential=self.credential) self.key_client = KeyClient(vault_url=Configuration.vault_url, credential=self.credential) self.key_ops = [ "encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey" ]
def test_client_secret_credential_cache(): expired = "this token's expired" now = time.time() expired_on = int(now - 300) expired_token = AccessToken(expired, expired_on) token_payload = { "access_token": expired, "expires_in": 0, "ext_expires_in": 0, "expires_on": expired_on, "not_before": now, "token_type": "Bearer", "resource": str(uuid.uuid1()), } mock_response = Mock( text=lambda: json.dumps(token_payload), headers={"content-type": "application/json"}, status_code=200, content_type=["application/json"], ) mock_send = Mock(return_value=mock_response) credential = ClientSecretCredential("client_id", "secret", tenant_id=str(uuid.uuid1()), transport=Mock(send=mock_send)) scopes = ("https://foo.bar/.default", "https://bar.qux/.default") token = credential.get_token(*scopes) assert token == expired_token token = credential.get_token(*scopes) assert token == expired_token assert mock_send.call_count == 2
def test_persistent_cache_linux(mock_extensions): """The credential should use an unencrypted cache when encryption is unavailable and the user explicitly opts in. This test was written when Linux was the only platform on which encryption may not be available. """ required_arguments = ("tenant-id", "client-id", "secret") # the credential should prefer an encrypted cache even when the user allows an unencrypted one ClientSecretCredential(*required_arguments, enable_persistent_cache=True, allow_unencrypted_cache=True) assert mock_extensions.PersistedTokenCache.called_with( mock_extensions.LibsecretPersistence) mock_extensions.PersistedTokenCache.reset_mock() # (when LibsecretPersistence's dependencies aren't available, constructing it raises ImportError) mock_extensions.LibsecretPersistence = Mock(side_effect=ImportError) # encryption unavailable, no opt in to unencrypted cache -> credential should raise with pytest.raises(ValueError): ClientSecretCredential(*required_arguments, enable_persistent_cache=True) ClientSecretCredential(*required_arguments, enable_persistent_cache=True, allow_unencrypted_cache=True) assert mock_extensions.PersistedTokenCache.called_with( mock_extensions.FilePersistence)
def test_enable_persistent_cache(): """the credential should use the persistent cache only when given enable_persistent_cache=True""" required_arguments = ("tenant-id", "client-id", "secret") persistent_cache = "azure.identity._internal.persistent_cache" # credential should default to an in memory cache raise_when_called = Mock(side_effect=Exception( "credential shouldn't attempt to load a persistent cache")) with patch(persistent_cache + "._load_persistent_cache", raise_when_called): ClientSecretCredential(*required_arguments) # allowing an unencrypted cache doesn't count as opting in to the persistent cache ClientSecretCredential(*required_arguments, allow_unencrypted_cache=True) # keyword argument opts in to persistent cache with patch(persistent_cache + ".msal_extensions") as mock_extensions: ClientSecretCredential(*required_arguments, enable_persistent_cache=True) assert mock_extensions.PersistedTokenCache.call_count == 1 # opting in on an unsupported platform raises an exception with patch(persistent_cache + ".sys.platform", "commodore64"): with pytest.raises(NotImplementedError): ClientSecretCredential(*required_arguments, enable_persistent_cache=True) with pytest.raises(NotImplementedError): ClientSecretCredential(*required_arguments, enable_persistent_cache=True, allow_unencrypted_cache=True)
def test_no_scopes(): """The credential should raise ValueError when get_token is called with no scopes""" credential = ClientSecretCredential("tenant-id", "client-id", "client-secret") with pytest.raises(ValueError): credential.get_token()
def test_regional_authority(): """the credential should configure MSAL with a regional authority specified via kwarg or environment variable""" mock_confidential_client = Mock(return_value=Mock( acquire_token_silent_with_error=lambda *_, **__: { "access_token": "**", "expires_in": 3600 })) for region in RegionalAuthority: mock_confidential_client.reset_mock() # region can be configured via environment variable with patch.dict( "os.environ", {EnvironmentVariables.AZURE_REGIONAL_AUTHORITY_NAME: region}, clear=True): credential = ClientSecretCredential("tenant", "client-id", "secret") with patch("msal.ConfidentialClientApplication", mock_confidential_client): credential.get_token("scope") assert mock_confidential_client.call_count == 1 _, kwargs = mock_confidential_client.call_args assert kwargs["azure_region"] == region
def test_multitenant_authentication_not_allowed(): expected_tenant = "expected-tenant" expected_token = "***" def send(request, **_): parsed = urlparse(request.url) if "/oauth2/v2.0/token" not in parsed.path: return get_discovery_response("https://{}/{}".format( parsed.netloc, expected_tenant)) tenant = parsed.path.split("/")[1] token = expected_token if tenant == expected_tenant else expected_token * 2 return mock_response(json_payload=build_aad_response( access_token=token)) credential = ClientSecretCredential(expected_tenant, "client-id", "secret", transport=Mock(send=send)) token = credential.get_token("scope") assert token.token == expected_token token = credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token with patch.dict( "os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): token = credential.get_token("scope", tenant_id="un" + expected_tenant) assert token.token == expected_token
def getAccessToken(config): print("Get AAD access token") aadTokenFile = '/home/ubuntu/aad_oauth_token.json' if os.name == 'nt': aadTokenFile = '__pycache__\\aad_oauth_token.json' if path.exists(aadTokenFile): f = open(aadTokenFile, ) data = json.load(f) expireOn = data[1] if int(time.time()) < expireOn: print('Reuse existing token') print("Access token lifetime: " + str(data[1] - int(time.time()))) return data[0] print("Get token from AzureAD") client = ClientSecretCredential(config["TenantId"], config["ClientId"], config["ClientSecret"], authority=config["Authority"]) accessToken = client.get_token(config["Scopes"]) print("Access token lifetime: " + str(accessToken[1] - int(time.time()))) pathSaveFile = '/home/ubuntu/aad_oauth_token.json' if os.name == 'nt': pathSaveFile = '__pycache__\\aad_oauth_token.json' with open(pathSaveFile, 'w') as outfile: json.dump(accessToken, outfile) return accessToken.token
def test_client_secret_credential_cache(): expired = "this token's expired" now = int(time.time()) expired_on = now - 3600 expired_token = AccessToken(expired, expired_on) token_payload = { "access_token": expired, "expires_in": 0, "ext_expires_in": 0, "expires_on": expired_on, "not_before": now, "token_type": "Bearer", } mock_send = Mock(return_value=mock_response(json_payload=token_payload)) scope = "scope" credential = ClientSecretCredential("client_id", "secret", tenant_id="some-guid", transport=Mock(send=mock_send)) # get_token initially returns the expired token because the credential # doesn't check whether tokens it receives from the service have expired token = credential.get_token(scope) assert token == expired_token access_token = "new token" token_payload["access_token"] = access_token token_payload["expires_on"] = now + 3600 valid_token = AccessToken(access_token, now + 3600) # second call should observe the cached token has expired, and request another token = credential.get_token(scope) assert token == valid_token assert mock_send.call_count == 2
def test_user_agent(): transport = msal_validating_transport( requests=[Request(required_headers={"User-Agent": USER_AGENT})], responses=[mock_response(json_payload=build_aad_response(access_token="**"))], ) credential = ClientSecretCredential("tenant-id", "client-id", "client-secret", transport=transport) credential.get_token("scope")
def test_live_multitenant_authentication(live_service_principal): # first create a credential with a non-existent tenant credential = ClientSecretCredential( "...", live_service_principal["client_id"], live_service_principal["client_secret"]) # then get a valid token for an actual tenant token = credential.get_token("https://vault.azure.net/.default", tenant_id=live_service_principal["tenant_id"]) assert token.token assert token.expires_on
def test_client_secret_credential(live_identity_settings): credential = ClientSecretCredential( live_identity_settings["client_id"], live_identity_settings["client_secret"], live_identity_settings["tenant_id"], ) token = credential.get_token(ARM_SCOPE) assert token assert token.token assert token.expires_on
def __init__(self, arguments): super().__init__(arguments) client_id = self.get_from_env("AZURE_CLIENT_ID") tenant_id = self.get_from_env("AZURE_TENANT_ID") secret = self.get_from_env("AZURE_CLIENT_SECRET") self.credential = ClientSecretCredential(tenant_id, client_id, secret) self.async_credential = AsyncClientSecretCredential( tenant_id, client_id, secret) self.scope = "https://vault.azure.net/.default"
def test_tenant_id_validation(): """The credential should raise ValueError when given an invalid tenant_id""" valid_ids = {"c878a2ab-8ef4-413b-83a0-199afb84d7fb", "contoso.onmicrosoft.com", "organizations", "common"} for tenant in valid_ids: ClientSecretCredential(tenant, "client-id", "secret") invalid_ids = {"", "my tenant", "my_tenant", "/", "\\", '"my-tenant"', "'my-tenant'"} for tenant in invalid_ids: with pytest.raises(ValueError): ClientSecretCredential(tenant, "client-id", "secret")
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_token_cache(): """the credential should default to an in memory cache, and optionally use a persistent cache""" with patch("azure.identity._persistent_cache.msal_extensions") as mock_msal_extensions: credential = ClientSecretCredential("tenant", "client-id", "secret") assert not mock_msal_extensions.PersistedTokenCache.called assert isinstance(credential._cache, TokenCache) ClientSecretCredential( "tenant", "client-id", "secret", cache_persistence_options=TokenCachePersistenceOptions() ) assert mock_msal_extensions.PersistedTokenCache.call_count == 1
def test_policies_configurable(): policy = Mock(spec_set=SansIOHTTPPolicy, on_request=Mock()) def send(*_, **__): return mock_response(json_payload=build_aad_response(access_token="**")) credential = ClientSecretCredential( "tenant-id", "client-id", "client-secret", policies=[ContentDecodePolicy(), policy], transport=Mock(send=send) ) credential.get_token("scope") assert policy.on_request.called
def init_access_token(self, ad_tenant_id: str = None, sp_client_id: str = None, ad_domain: str = None, debug_mode: bool = False) -> str: """ Initialize access token to access ygdra api. Parameters ---------- ad_tenant_id : str Tenant id. If None (default), will parse AD_TENANT_ID environment variable. sp_client_id: str Service principal client id. If None (default), will parse SP_CLIENT_ID environment variable. ad_domain: str Principal domain. If None (default), will parse AD_DOMAIN environment variable. debug_mode: bool Indicates if you want output intermediate steps. False by default. """ if not ad_tenant_id: ad_tenant_id = os.getenv('AD_TENANT_ID') if not sp_client_id: sp_client_id = os.getenv('SP_CLIENT_ID') if not ad_domain: ad_domain = os.getenv('AD_DOMAIN') if not ad_tenant_id: raise ValueError('ad_tenant_id input value is null or empty') if not sp_client_id: raise ValueError('sp_client_id input value is null or empty') if not ad_domain: raise ValueError('ad_domain input value is null or empty') credential = ClientSecretCredential(ad_tenant_id, sp_client_id, self.sp_client_secret) scope = f'https://{ad_domain}/{sp_client_id}/.default' if (debug_mode): print(f'tenantId: {ad_tenant_id}') print(f'clientId: {sp_client_id}') print(f'domain: {ad_domain}') print(f'scope: {scope}') access_token = credential.get_token(scope) self.token = access_token.token return access_token.token
def test_policies_configurable(): policy = Mock(spec_set=SansIOHTTPPolicy, on_request=Mock()) transport = msal_validating_transport( requests=[Request()], responses=[mock_response(json_payload=build_aad_response(access_token="**"))], ) credential = ClientSecretCredential( "tenant-id", "client-id", "client-secret", policies=[ContentDecodePolicy(), policy], transport=transport ) credential.get_token("scope") assert policy.on_request.called
def test_authority(authority): """the credential should accept an authority, with or without scheme, as an argument or environment variable""" tenant_id = "expected-tenant" parsed_authority = urlparse(authority) expected_netloc = parsed_authority.netloc or authority expected_authority = "https://{}/{}".format(expected_netloc, tenant_id) mock_ctor = Mock( return_value=Mock(acquire_token_silent_with_error=lambda *_, **__: {"access_token": "**", "expires_in": 42}) ) credential = ClientSecretCredential(tenant_id, "client-id", "secret", authority=authority) with patch("msal.ConfidentialClientApplication", mock_ctor): # must call get_token because the credential constructs the MSAL application lazily credential.get_token("scope") assert mock_ctor.call_count == 1 _, kwargs = mock_ctor.call_args assert kwargs["authority"] == expected_authority mock_ctor.reset_mock() # authority can be configured via environment variable with patch.dict("os.environ", {EnvironmentVariables.AZURE_AUTHORITY_HOST: authority}, clear=True): credential = ClientSecretCredential(tenant_id, "client-id", "secret") with patch("msal.ConfidentialClientApplication", mock_ctor): credential.get_token("scope") assert mock_ctor.call_count == 1 _, kwargs = mock_ctor.call_args assert kwargs["authority"] == expected_authority
def authenticate_sp(self, tenant_id: str = None, client_id: str = None, client_secret: str = None) -> Credentials: """ Implements authentication for the Azure provider """ try: # Set logging level to error for libraries as otherwise generates a lot of warnings logging.getLogger('adal-python').setLevel(logging.ERROR) logging.getLogger('msrest').setLevel(logging.ERROR) logging.getLogger('msrestazure.azure_active_directory').setLevel( logging.ERROR) logging.getLogger('urllib3').setLevel(logging.ERROR) logging.getLogger( 'azure.core.pipeline.policies.http_logging_policy').setLevel( logging.ERROR) arm_credentials = ClientSecretCredential( client_id=client_id, client_secret=client_secret, tenant_id=tenant_id, ) aad_graph_credentials = ClientSecretCredential( client_id=client_id, client_secret=client_secret, tenant_id=tenant_id, resource='https://graph.windows.net', ) return Credentials( arm_credentials, aad_graph_credentials, tenant_id=tenant_id, current_user=client_id, ) except HttpResponseError as e: if ', AdalError: Unsupported wstrust endpoint version. ' \ 'Current supported version is wstrust2005 or wstrust13.' in e.args: logger.error( f'You are likely authenticating with a Microsoft Account. \ This authentication mode only supports Azure Active Directory principal authentication.\ {e}', ) raise e
def generate_async_oauth_token(self): from azure.identity.aio import ClientSecretCredential return ClientSecretCredential( self.settings.ACTIVE_DIRECTORY_TENANT_ID, self.settings.ACTIVE_DIRECTORY_APPLICATION_ID, self.settings.ACTIVE_DIRECTORY_APPLICATION_SECRET, )
def test_client_secret_credential(): client_id = "fake-client-id" secret = "fake-client-secret" tenant_id = "fake-tenant-id" access_token = "***" transport = msal_validating_transport( endpoint="https://localhost/" + tenant_id, requests=[ Request(url_substring=tenant_id, required_data={ "client_id": client_id, "client_secret": secret }) ], responses=[ mock_response(json_payload=build_aad_response( access_token=access_token)) ], ) token = ClientSecretCredential(tenant_id, client_id, secret, transport=transport).get_token("scope") assert token.token == access_token
def azure_credentials(azure_cfg: AzureCfg, pytestconfig): if pytestconfig.getoption('local'): return AzureCliCredential() else: return ClientSecretCredential(client_id=azure_cfg.client_id, client_secret=azure_cfg.client_secret, tenant_id=azure_cfg.tenant_id)
def _login(self) -> Boolean: """Login into MS Azure Cloud account and get usable client object(s). :return: Boolean True value depends on successful login operation """ try: subscription_id: str = self.config["subscription_id"] self.tenant_id = self.config["tenant_id"] client_id = self.config["client_id"] client_secret = self.config["client_secret"] credential = ClientSecretCredential( tenant_id=self.tenant_id, client_id=client_id, client_secret=client_secret, ) self.compute_client = ComputeManagementClient( credential=credential, subscription_id=subscription_id ) self.resource_client = ResourceManagementClient( credential=credential, subscription_id=subscription_id ) self.network_client = NetworkManagementClient( credential=credential, subscription_id=subscription_id ) logging.info("logging in Microsoft Azure Cloud={}".format(self.name)) return True except Error as e: logging.error(e) return False
def test_client_secret_credential(): client_id = "fake-client-id" secret = "fake-client-secret" tenant_id = "fake-tenant-id" access_token = "***" transport = validating_transport( requests=[ Request(url_substring=tenant_id, required_data={ "client_id": client_id, "client_secret": secret }) ], responses=[ mock_response( json_payload={ "token_type": "Bearer", "expires_in": 42, "ext_expires_in": 42, "access_token": access_token, }) ], ) token = ClientSecretCredential(client_id=client_id, secret=secret, tenant_id=tenant_id, transport=transport).get_token("scope") # not validating expires_on because doing so requires monkeypatching time, and this is tested elsewhere assert token.token == access_token
def test_client_secret_credential(live_service_principal): credential = ClientSecretCredential( live_service_principal["tenant_id"], live_service_principal["client_id"], live_service_principal["client_secret"], ) get_token(credential)
def get_azure_auth( azure_config: AzureConfig ) -> Union[DefaultAzureCredential, ClientSecretCredential]: """ Returns the authentication object for the azure.identity library, based on either the chosen Service Principal (if set, and if the password was found), or the interactive browser authentication if not all Service Principal information is available. :param azure_config: The object containing all Azure-related information. :return: An azure.identity authentication object. """ secrets_handler = SecretsHandling(project_root=azure_config.project_root) application_key = secrets_handler.get_secret_from_environment( fixed_paths.SERVICE_PRINCIPAL_KEY, allow_missing=True) if not azure_config.tenant_id: raise ValueError( "No tenant_id field was found. Please complete the Azure setup.") if application_key and azure_config.application_id: return ClientSecretCredential(tenant_id=azure_config.tenant_id, client_id=azure_config.application_id, client_secret=application_key) logging.warning( "Unable to retrieve the key for the Service Principal authentication " f"(expected in environment variable '{fixed_paths.SERVICE_PRINCIPAL_KEY}' or YAML). " f"Switching to interactive login.") return DefaultAzureCredential()