def test_persistent_cache_multiple_clients():
    """the credential shouldn't use tokens issued to other service principals"""

    access_token_a = "token a"
    access_token_b = "not " + access_token_a
    transport_a = msal_validating_transport(
        requests=[Request()], responses=[mock_response(json_payload=build_aad_response(access_token=access_token_a))]
    )
    transport_b = msal_validating_transport(
        requests=[Request()], responses=[mock_response(json_payload=build_aad_response(access_token=access_token_b))]
    )

    cache = TokenCache()
    with patch("azure.identity._internal.persistent_cache._load_persistent_cache") as mock_cache_loader:
        mock_cache_loader.return_value = Mock(wraps=cache)
        credential_a = ClientSecretCredential(
            "tenant-id", "client-a", "...", _enable_persistent_cache=True, transport=transport_a
        )
        assert mock_cache_loader.call_count == 1, "credential should load the persistent cache"
        credential_b = ClientSecretCredential(
            "tenant-id", "client-b", "...", _enable_persistent_cache=True, transport=transport_b
        )
        assert mock_cache_loader.call_count == 2, "credential should load the persistent cache"

    # A caches a token
    scope = "scope"
    token_a = credential_a.get_token(scope)
    assert token_a.token == access_token_a
    assert transport_a.send.call_count == 3  # two MSAL discovery requests, one token request

    # B should get a different token for the same scope
    token_b = credential_b.get_token(scope)
    assert token_b.token == access_token_b
    assert transport_b.send.call_count == 3
Пример #2
0
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()
Пример #3
0
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
Пример #4
0
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_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 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
Пример #7
0
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
Пример #8
0
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_request_url(authority):
    """the credential should accept an authority, with or without scheme, as an argument or environment variable"""

    tenant_id = "expected_tenant"
    access_token = "***"
    parsed_authority = urlparse(authority)
    expected_netloc = parsed_authority.netloc or authority  # "localhost" parses to netloc "", path "localhost"

    def mock_send(request, **kwargs):
        actual = urlparse(request.url)
        assert actual.scheme == "https"
        assert actual.netloc == expected_netloc
        assert actual.path.startswith("/" + tenant_id)
        return mock_response(json_payload={"token_type": "Bearer", "expires_in": 42, "access_token": access_token})

    credential = ClientSecretCredential(
        tenant_id, "client-id", "secret", transport=Mock(send=mock_send), authority=authority
    )
    token = credential.get_token("scope")
    assert token.token == access_token

    # 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", transport=Mock(send=mock_send))
        credential.get_token("scope")
    assert token.token == access_token
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_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 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
Пример #13
0
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
Пример #14
0
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
Пример #15
0
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
Пример #16
0
def test_multitenant_authentication_not_allowed():
    """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)"""

    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

    # explicitly specifying the configured tenant is okay
    token = credential.get_token("scope", tenant_id=expected_tenant)
    assert token.token == expected_token

    # but any other tenant should get an error
    with pytest.raises(ClientAuthenticationError,
                       match="allow_multitenant_authentication"):
        credential.get_token("scope", tenant_id="un" + expected_tenant)

    # ...unless the compat switch is enabled
    with patch.dict("os.environ", {
            EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION:
            "true"
    }):
        token = credential.get_token("scope", tenant_id="un" + expected_tenant)
    assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled"
Пример #17
0
    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
Пример #18
0
def test_allow_multitenant_authentication():
    """When allow_multitenant_authentication is True, the credential should respect get_token(tenant_id=...)"""

    first_tenant = "first-tenant"
    first_token = "***"
    second_tenant = "second-tenant"
    second_token = first_token * 2

    def send(request, **_):
        parsed = urlparse(request.url)
        tenant = parsed.path.split("/")[1]
        assert tenant in (first_tenant, second_tenant,
                          "common"), 'unexpected tenant "{}"'.format(tenant)
        if "/oauth2/v2.0/token" not in parsed.path:
            return get_discovery_response("https://{}/{}".format(
                parsed.netloc, tenant))

        token = first_token if tenant == first_tenant else second_token
        return mock_response(json_payload=build_aad_response(
            access_token=token))

    credential = ClientSecretCredential(first_tenant,
                                        "client-id",
                                        "secret",
                                        allow_multitenant_authentication=True,
                                        transport=Mock(send=send))
    token = credential.get_token("scope")
    assert token.token == first_token

    token = credential.get_token("scope", tenant_id=first_tenant)
    assert token.token == first_token

    token = credential.get_token("scope", tenant_id=second_tenant)
    assert token.token == second_token

    # should still default to the first tenant
    token = credential.get_token("scope")
    assert token.token == first_token
Пример #19
0
class PersistentCacheRead(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")
        cache_options = TokenCachePersistenceOptions(
            allow_unencrypted_storage=True)
        self.credential = ClientSecretCredential(
            tenant_id,
            client_id,
            secret,
            cache_persistence_options=cache_options)
        self.async_credential = AsyncClientSecretCredential(
            tenant_id,
            client_id,
            secret,
            cache_persistence_options=cache_options)
        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 test_request_url():
    authority = "localhost"
    tenant_id = "expected_tenant"
    access_token = "***"

    def mock_send(request, **kwargs):
        parsed = urlparse(request.url)
        assert parsed.scheme == "https"
        assert parsed.netloc == authority
        assert parsed.path.startswith("/" + tenant_id)

        return mock_response(json_payload={"token_type": "Bearer", "expires_in": 42, "access_token": access_token})

    credential = ClientSecretCredential(
        tenant_id, "client-id", "secret", transport=Mock(send=mock_send), authority=authority
    )
    token = credential.get_token("scope")
    assert token.token == access_token

    # 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", transport=Mock(send=mock_send))
        credential.get_token("scope")
    assert token.token == access_token
Пример #21
0
    def getAccessToken(self, config):
        print("Get AAD access token")

        if (self.in_memory_token != None
                and int(time.time()) < self.in_memory_token[1]):
            print("Return token from memory")
            print("Access token lifetime: " +
                  str(self.in_memory_token[1] - int(time.time())))
            return self.in_memory_token[0]

        aadTokenFile = '/home/ubuntu/aad_oauth_token.json'
        if os.name == 'nt':
            aadTokenFile = '__pycache__\\aad_oauth_token.json'
        if path.exists(aadTokenFile):
            f = open(aadTokenFile, )
            self.in_memory_token = json.load(f)
            expireOn = self.in_memory_token[1]
            if int(time.time()) < expireOn:
                print('Reuse existing token')
                print("Access token lifetime: " +
                      str(self.in_memory_token[1] - int(time.time())))
                return self.in_memory_token[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())))

        try:
            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)
        except:
            print("Error saving token to file")

        self.in_memory_token = accessToken

        return accessToken.token
 def get_access_token(tenant_id, client_id, client_secret, scope):
     credentials = ClientSecretCredential(tenant_id=tenant_id,
                                          client_id=client_id,
                                          client_secret=client_secret)
     return credentials.get_token(scope)
Пример #23
0
from sys import path
path.append('\\Program Files\\Microsoft.NET\\ADOMD.NET\\150')
from pyadomd import Pyadomd

from azure.identity import ClientSecretCredential

scope = 'https://northeurope.asazure.windows.net/.default'

tenant_id = input('Tenant id: ')
client_id = input('Client id: ')
client_secret = input('Client secret: ')
authority = f'https://login.microsoftonline.com/'

credential = ClientSecretCredential(tenant_id, client_id, client_secret, authority=authority)
token = credential.get_token(scope)
serverName = 'asazure://northeurope.asazure.windows.net/aast'

connectionString = f'Provider=MSOLAP;Data Source={serverName};Initial Catalog=Test;User ID=;Password={token.token};Persist Security Info=True;Impersonation Level=Impersonate;'

query = "EVALUATE SUMMARIZECOLUMNS('Sample Data'[Value1])"

with Pyadomd(connectionString) as conn:
    with conn.cursor().execute(query) as cur:
        print(cur.fetchall())