Example #1
0
def test_no_scopes():
    """The credential should raise when get_token is called with no scopes"""

    credential = UsernamePasswordCredential("client-id", "username",
                                            "password")
    with pytest.raises(ValueError):
        credential.get_token()
def test_username_password_credential():
    expected_token = "access-token"
    client_id = "client-id"
    transport = validating_transport(
        requests=[Request()] *
        3,  # not validating requests because they're formed by MSAL
        responses=[
            # tenant discovery
            mock_response(
                json_payload={
                    "authorization_endpoint": "https://a/b",
                    "token_endpoint": "https://a/b"
                }),
            # user realm discovery, interests MSAL only when the response body contains account_type == "Federated"
            mock_response(json_payload={}),
            # token request
            mock_response(
                json_payload=build_aad_response(access_token=expected_token,
                                                id_token=build_id_token(
                                                    aud=client_id))),
        ],
    )

    credential = UsernamePasswordCredential(
        client_id=client_id,
        username="******",
        password="******",
        transport=transport,
        instance_discovery=
        False,  # kwargs are passed to MSAL; this one prevents an AAD verification request
    )

    token = credential.get_token("scope")
    assert token.token == expected_token
Example #3
0
def test_username_password_credential():
    expected_token = "access-token"
    transport = validating_transport(
        requests=[Request()] *
        2,  # not validating requests because they're formed by MSAL
        responses=[
            # expecting tenant discovery then a token request
            mock_response(
                json_payload={
                    "authorization_endpoint": "https://a/b",
                    "token_endpoint": "https://a/b"
                }),
            mock_response(
                json_payload={
                    "access_token": expected_token,
                    "expires_in": 42,
                    "token_type": "Bearer",
                    "ext_expires_in": 42,
                }),
        ],
    )

    credential = UsernamePasswordCredential(
        client_id="some-guid",
        username="******",
        password="******",
        transport=transport,
        instance_discovery=
        False,  # kwargs are passed to MSAL; this one prevents an AAD verification request
    )

    token = credential.get_token("scope")
    assert token.token == expected_token
Example #4
0
    def __init__(self, username, password, tenant_id=None, authority=None):
        self.creds = UsernamePasswordCredential(
            DEVELOPER_SIGN_ON_CLIENT_ID,
            username,
            password,
            tenant_id=tenant_id,
            authority=authority,
        )

        # Wrap the credential's get_token method to ensure that only one thread
        # calls get_token with a specific scope. All other threads requesting a
        # token of the same scope will be blocked until the first has completed.
        # Thus pre-filling the MSAL token cache. It prevents a bunch of
        # unnecessary network requets for authentication.
        self.creds.get_token = _wait_once_per_scope(self.creds.get_token)

        # Request a token now as it will validate the user supplied the correct
        # username and password. If we did not catch that here, then each worker
        # would end up failing---possibly locking the user's account.
        #
        # Is using this scope a safe assumption, will all users have access to
        # this scope? I'd really like to validate a person's credential before
        # we let the workers loose, but it's impossible to know what Azure SDK
        # clients will be used by the user, which dictates the scope used.
        self.creds.get_token(ARM_SCOPE)
def test_user_agent():
    transport = validating_transport(
        requests=[Request()] * 2 + [Request(required_headers={"User-Agent": USER_AGENT})],
        responses=[get_discovery_response()] * 2
        + [mock_response(json_payload=build_aad_response(access_token="**", id_token=build_id_token()))],
    )

    credential = UsernamePasswordCredential("client-id", "username", "password", transport=transport)

    credential.get_token("scope")
Example #6
0
    def test_obo_cert(self):
        client_id = self.obo_settings["client_id"]
        tenant_id = self.obo_settings["tenant_id"]

        user_credential = UsernamePasswordCredential(
            client_id, self.obo_settings["username"], self.obo_settings["password"], tenant_id=tenant_id
        )
        assertion = user_credential.get_token(self.obo_settings["scope"]).token
        credential = OnBehalfOfCredential(tenant_id, client_id, client_certificate=self.obo_settings["cert_bytes"], user_assertion=assertion)
        credential.get_token(self.obo_settings["scope"])
def test_authenticate():
    client_id = "client-id"
    environment = "localhost"
    issuer = "https://" + environment
    tenant_id = "some-tenant"
    authority = issuer + "/" + tenant_id

    access_token = "***"
    scope = "scope"

    # mock AAD response with id token
    object_id = "object-id"
    home_tenant = "home-tenant-id"
    username = "******"
    id_token = build_id_token(aud=client_id,
                              iss=issuer,
                              object_id=object_id,
                              tenant_id=home_tenant,
                              username=username)
    auth_response = build_aad_response(uid=object_id,
                                       utid=home_tenant,
                                       access_token=access_token,
                                       refresh_token="**",
                                       id_token=id_token)

    transport = validating_transport(
        requests=[Request(url_substring=issuer)] * 4,
        responses=[
            get_discovery_response(authority),  # instance discovery
            get_discovery_response(authority),  # tenant discovery
            mock_response(status_code=404),  # user realm discovery
            mock_response(json_payload=auth_response
                          ),  # token request following authenticate()
        ],
    )

    credential = UsernamePasswordCredential(
        username=username,
        password="******",
        authority=environment,
        client_id=client_id,
        tenant_id=tenant_id,
        transport=transport,
    )
    record = credential.authenticate(scopes=(scope, ))
    for auth_record in (record, credential.authentication_record):
        assert auth_record.authority == environment
        assert auth_record.home_account_id == object_id + "." + home_tenant
        assert auth_record.tenant_id == home_tenant
        assert auth_record.username == username

    # credential should have a cached access token for the scope passed to authenticate
    token = credential.get_token(scope)
    assert token.token == access_token
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:
        UsernamePasswordCredential("client-id", "username", "password", tenant_id=tenant)

    invalid_ids = {"my tenant", "my_tenant", "/", "\\", '"my-tenant"', "'my-tenant'"}
    for tenant in invalid_ids:
        with pytest.raises(ValueError):
            UsernamePasswordCredential("client-id", "username", "password", tenant_id=tenant)
def test_client_capabilities():
    """the credential should configure MSAL for capability CP1 (ability to handle claims challenges)"""

    transport = Mock(send=Mock(side_effect=Exception("this test mocks MSAL, so no request should be sent")))
    credential = UsernamePasswordCredential("client-id", "username", "password", transport=transport)

    with patch("msal.PublicClientApplication") as PublicClientApplication:
        credential._get_app()

    assert PublicClientApplication.call_count == 1
    _, kwargs = PublicClientApplication.call_args
    assert kwargs["client_capabilities"] == ["CP1"]
Example #10
0
class CredsViaUsernamePassword(SessionProvider):
    """A session provider that obtains Azure tokens via username & password.

    Credentials are obtained by authenticating with Azure AD using a username
    and password. Access tokens are cached by the underlying MSAL library and
    automatically refreshed as needed.

    The `username` is typically an email address that specifies the user.
    `Password` is the password for the specified username.

    The `tenant_id` argument is optional specifies the tenant of the user.
    Normally, this can be derived from the email address used as the username,
    so it is not required.

    The `authority` argument is optional and specifies the Microsoft authority
    host to use. If none is provided, the default is
    "login.microsoftonline.com".

    For more information, see [Azure SDK
    documentation](https://azuresdkdocs.blob.core.windows.net/$web/python/azure-identity/1.4.0/azure.identity.html#azure.identity.UsernamePasswordCredential)
    """

    def __init__(self, username, password, tenant_id=None, authority=None):
        self.creds = UsernamePasswordCredential(
            DEVELOPER_SIGN_ON_CLIENT_ID,
            username,
            password,
            tenant_id=tenant_id,
            authority=authority,
        )

        # Wrap the credential's get_token method to ensure that only one thread
        # calls get_token with a specific scope. All other threads requesting a
        # token of the same scope will be blocked until the first has completed.
        # Thus pre-filling the MSAL token cache. It prevents a bunch of
        # unnecessary network requets for authentication.
        self.creds.get_token = _wait_once_per_scope(self.creds.get_token)

        # Request a token now as it will validate the user supplied the correct
        # username and password. If we did not catch that here, then each worker
        # would end up failing---possibly locking the user's account.
        #
        # Is using this scope a safe assumption, will all users have access to
        # this scope? I'd really like to validate a person's credential before
        # we let the workers loose, but it's impossible to know what Azure SDK
        # clients will be used by the user, which dictates the scope used.
        self.creds.get_token(ARM_SCOPE)

    def session(self, _subscription_id):
        # The same credentials are used regardless of the subscription within a
        # tenant, which is why we ignore the subscription here--unlike AWS.
        return self.creds
def test_policies_configurable():
    policy = Mock(spec_set=SansIOHTTPPolicy, on_request=Mock())

    transport = validating_transport(
        requests=[Request()] * 3,
        responses=[get_discovery_response()] * 2
        + [mock_response(json_payload=build_aad_response(access_token="**", id_token=build_id_token()))],
    )
    credential = UsernamePasswordCredential("client-id", "username", "password", policies=[policy], transport=transport)

    credential.get_token("scope")

    assert policy.on_request.called
def test_claims_challenge():
    """get_token should and authenticate pass any claims challenge to MSAL token acquisition APIs"""

    msal_acquire_token_result = dict(
        build_aad_response(access_token="**", id_token=build_id_token()),
        id_token_claims=id_token_claims("issuer", "subject", "audience", upn="upn"),
    )
    expected_claims = '{"access_token": {"essential": "true"}'

    transport = Mock(send=Mock(side_effect=Exception("this test mocks MSAL, so no request should be sent")))
    credential = UsernamePasswordCredential("client-id", "username", "password", transport=transport)
    with patch.object(UsernamePasswordCredential, "_get_app") as get_mock_app:
        msal_app = get_mock_app()
        msal_app.acquire_token_by_username_password.return_value = msal_acquire_token_result

        credential.authenticate(scopes=["scope"], claims=expected_claims)
        assert msal_app.acquire_token_by_username_password.call_count == 1
        args, kwargs = msal_app.acquire_token_by_username_password.call_args
        assert kwargs["claims_challenge"] == expected_claims

        credential.get_token("scope", claims=expected_claims)

        assert msal_app.acquire_token_by_username_password.call_count == 2
        args, kwargs = msal_app.acquire_token_by_username_password.call_args
        assert kwargs["claims_challenge"] == expected_claims

        msal_app.get_accounts.return_value = [{"home_account_id": credential._auth_record.home_account_id}]
        msal_app.acquire_token_silent_with_error.return_value = msal_acquire_token_result
        credential.get_token("scope", claims=expected_claims)

        assert msal_app.acquire_token_silent_with_error.call_count == 1
        args, kwargs = msal_app.acquire_token_silent_with_error.call_args
        assert kwargs["claims_challenge"] == expected_claims
def test_client_capabilities():
    """the credential should configure MSAL for capability CP1 unless AZURE_IDENTITY_DISABLE_CP1 is set"""

    transport = Mock(send=Mock(side_effect=Exception(
        "this test mocks MSAL, so no request should be sent")))

    credential = UsernamePasswordCredential("client-id",
                                            "username",
                                            "password",
                                            transport=transport)
    with patch("msal.PublicClientApplication") as PublicClientApplication:
        credential._get_app()

    assert PublicClientApplication.call_count == 1
    _, kwargs = PublicClientApplication.call_args
    assert kwargs["client_capabilities"] == ["CP1"]

    credential = UsernamePasswordCredential("client-id",
                                            "username",
                                            "password",
                                            transport=transport)
    with patch.dict("os.environ", {"AZURE_IDENTITY_DISABLE_CP1": "true"}):
        with patch("msal.PublicClientApplication") as PublicClientApplication:
            credential._get_app()

    assert PublicClientApplication.call_count == 1
    _, kwargs = PublicClientApplication.call_args
    assert kwargs["client_capabilities"] is None
Example #14
0
    async def test_obo(self):
        client_id = self.obo_settings["client_id"]
        client_secret = self.obo_settings["client_secret"]
        tenant_id = self.obo_settings["tenant_id"]

        user_credential = UsernamePasswordCredential(
            client_id,
            self.obo_settings["username"],
            self.obo_settings["password"],
            tenant_id=tenant_id)
        assertion = user_credential.get_token(self.obo_settings["scope"]).token
        credential = OnBehalfOfCredential(tenant_id, client_id, client_secret,
                                          assertion)
        await credential.get_token(self.obo_settings["scope"])
Example #15
0
def azresourcelist(
    username: str = typer.Option(..., "-u", help="Username"),
    password: str = typer.Option(...,
                                 "-p",
                                 prompt=True,
                                 hide_input=True,
                                 help="Password"),
):
    """Bruteforces password for an Azure account"""

    resources = {}
    cred = UsernamePasswordCredential("1950a258-227b-4e31-a9cf-717495945fc2",
                                      username, password)
    sub_client = SubscriptionClient(cred)
    for sub in sub_client.subscriptions.list():
        resources[sub.subscription_id] = [
            r.id for r in ResourceManagementClient(
                cred, sub.subscription_id).resources.list()
        ]

    if not resources:
        raise CommandUnsuccessfulError(f"Did not find resources as {username}")

    typer.echo(pformat(resources, indent=4))
    return {"resources": resources}
Example #16
0
    def get_credentials(self):
        """ Get credentials """
        tenant_id = self.config.get('azure.tenant_id')
        client_id = self.config.get('azure.client_id')
        client_secret = self.config.get('azure.client_secret')
        certificate_path = self.config.get('azure.client_certificate_path')
        username = self.config.get('azure.username')
        password = self.config.get('azure.password')

        if tenant_id and client_id and client_secret:
            return ClientSecretCredential(tenant_id=tenant_id,
                                          client_id=client_id,
                                          client_secret=client_secret)

        if tenant_id and client_id and certificate_path:
            return CertificateCredential(tenant_id=tenant_id,
                                         client_id=client_id,
                                         certificate_path=certificate_path)

        if client_id and username and password:
            return UsernamePasswordCredential(client_id=client_id,
                                              username=username,
                                              password=password)

        return DefaultAzureCredential()
Example #17
0
def test_username_password_auth(live_user_details):
    credential = UsernamePasswordCredential(
        client_id=live_user_details["client_id"],
        username=live_user_details["username"],
        password=live_user_details["password"],
        tenant_id=live_user_details["tenant"],
    )
    get_token(credential)
Example #18
0
def azbrute(
        username: str = typer.Option(..., "-u", help="Username to bruteforce"),
        passList: Path = typer.Option(..., "-p", help="Path to password list"),
):
    """Bruteforces password for an Azure account"""
    validCred = {"user": username, "pass": None}
    with open(passList, "r", encoding="latin-1") as passes:
        for pwd in passes.read().splitlines():
            login = UsernamePasswordCredential(
                "1950a258-227b-4e31-a9cf-717495945fc2", username, pwd)
            try:
                token = login.get_token("https://graph.microsoft.com/.default")
                validCred["pass"] = pwd
            except ClientAuthenticationError as e:
                pass

    if not validCred["pass"]:
        raise CommandUnsuccessfulError(
            f"Brute force for {username} unsuccessful")
    typer.echo(f"\nCredential found! - {validCred}\n")
def test_cache_persistence():
    """The credential should cache only in memory"""

    expected_cache = Mock()
    raise_when_called = Mock(side_effect=Exception(
        "credential shouldn't attempt to load a persistent cache"))
    with patch.multiple("msal_extensions.token_cache",
                        WindowsTokenCache=raise_when_called):
        with patch("msal.TokenCache", Mock(return_value=expected_cache)):
            credential = UsernamePasswordCredential("...", "...", "...")

    assert credential._cache is expected_cache
    def test_username_password(self):
        if self.is_live and not ("username" in self.cae_settings
                                 and "password" in self.cae_settings):
            pytest.skip("Missing a username or password for CAE test")

        credential = UsernamePasswordCredential(
            DEVELOPER_SIGN_ON_CLIENT_ID,
            authority=self.cae_settings["authority"],
            tenant_id=self.cae_settings["tenant_id"],
            username=self.cae_settings["username"],
            password=self.cae_settings["password"],
        )
        self.cae_test(credential)
Example #21
0
def _generate_credential(auth_method: str, token_path: Path, **kwargs):
    if auth_method == AuthMethod.DeviceCode.name:
        credential = DeviceCodeCredential(
            client_id=kwargs['client_id'],
            client_secret=kwargs['client_secret'],
            disable_automatic_authentication=True,
            tenant_id=kwargs['tenant_id'],
            **_get_cache_args(token_path))
    elif auth_method == AuthMethod.UsernamePassword.name:
        credential = UsernamePasswordCredential(
            client_id=kwargs['client_id'],
            client_credential=kwargs['client_secret'],
            disable_automatic_authentication=True,
            username=kwargs['username'],
            password=kwargs['password'],
            **_get_cache_args(token_path))
    elif auth_method == AuthMethod.ClientSecret.name:
        credential = ClientSecretCredential(
            client_id=kwargs['client_id'],
            tenant_id=kwargs['tenant_id'],
            client_secret=kwargs['client_secret'])
    else:
        raise RuntimeError(f'Auth method {auth_method} not found')
    return credential