Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Final azure-identity preview 2 changes #6664

Merged
merged 11 commits into from
Aug 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions sdk/identity/azure-identity/HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Release History

## 1.0.0b2 (2019-08-05)
### Breaking changes:
- Removed `azure.core.Configuration` from the public API in preparation for a
revamped configuration API. Static `create_config` methods have been renamed
`_create_config`, and will be removed in a future release.

### Dependency changes:
- Adopted [azure-core](https://pypi.org/project/azure-core/) 1.0.0b2
- If you later want to revert to azure-identity 1.0.0b1, or another Azure SDK
library requiring azure-core 1.0.0b1, you'll need to `pip uninstall azure-core`
- Adopted [MSAL](https://pypi.org/project/msal/) 0.4.1
- New dependency for Python 2.7: [mock](https://pypi.org/project/mock/)

### New features:
- Added credentials for authenticating users:
[`DeviceCodeCredential`](https://azure.github.io/azure-sdk-for-python/ref/azure.identity.html#azure.identity.DeviceCodeCredential),
[`InteractiveBrowserCredential`](https://azure.github.io/azure-sdk-for-python/ref/azure.identity.html#azure.identity.InteractiveBrowserCredential),
[`UsernamePasswordCredential`](https://azure.github.io/azure-sdk-for-python/ref/azure.identity.html#azure.identity.UsernamePasswordCredential)
- async versions of these credentials will be added in a future release

## 1.0.0b1 (2019-06-28)
Version 1.0.0b1 is the first preview of our efforts to create a user-friendly
and Pythonic authentication API for Azure SDK client libraries. For more
Expand Down
59 changes: 40 additions & 19 deletions sdk/identity/azure-identity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ It supports token authentication using an Azure Active Directory
This library is in preview and currently supports:
- [Service principal authentication](https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals)
- [Managed identity authentication](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview)
- User authentication

[Source code](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/identity/azure-identity/azure/identity)
| [Package (PyPI)](https://pypi.org/project/azure-identity/)
Expand Down Expand Up @@ -61,18 +62,22 @@ configuration:

|credential class|identity|configuration
|-|-|-
|`DefaultAzureCredential`|service principal or managed identity|none for managed identity; [environment variables](#environment-variables) for service principal
|`DefaultAzureCredential`|service principal, managed identity or user|none for managed identity; [environment variables](#environment-variables) for service principal or user authentication
|`ManagedIdentityCredential`|managed identity|none
|`EnvironmentCredential`|service principal|[environment variables](#environment-variables)
|`ClientSecretCredential`|service principal|constructor parameters
|`CertificateCredential`|service principal|constructor parameters
|[`DeviceCodeCredential`](https://azure.github.io/azure-sdk-for-python/ref/azure.identity.html#azure.identity.credentials.DeviceCodeCredential)|user|constructor parameters
|[`InteractiveBrowserCredential`](https://azure.github.io/azure-sdk-for-python/ref/azure.identity.html#azure.identity.InteractiveBrowserCredential)|user|constructor parameters
|[`UsernamePasswordCredential`](https://azure.github.io/azure-sdk-for-python/ref/azure.identity.html#azure.identity.credentials.UsernamePasswordCredential)|user|constructor parameters

Credentials can be chained together and tried in turn until one succeeds; see
[chaining credentials](#chaining-credentials) for details.

All credentials have an async equivalent in the `azure.identity.aio` namespace.
These are supported on Python 3.5.3+. See the
[async credentials](#async-credentials) example for details.
Service principal and managed identity credentials have an async equivalent in
the `azure.identity.aio` namespace, supported on Python 3.5.3+. See the
[async credentials](#async-credentials) example for details. Async user
credentials will be part of a future release.

## DefaultAzureCredential
`DefaultAzureCredential` is appropriate for most applications intended to run
Expand All @@ -90,18 +95,34 @@ for more information.

## Environment variables

`DefaultAzureCredential` and `EnvironmentCredential` are configured for service
principal authentication with these environment variables:

|variable name|value
|-|-
|`AZURE_CLIENT_ID`|service principal's app id
|`AZURE_TENANT_ID`|id of the principal's Azure Active Directory tenant
|`AZURE_CLIENT_SECRET`|one of the service principal's client secrets
|`AZURE_CLIENT_CERTIFICATE_PATH`|path to a PEM-encoded certificate file including private key (without password)

Either `AZURE_CLIENT_SECRET` or `AZURE_CLIENT_CERTIFICATE_PATH` must be set.
If both are set, the client secret will be used.
`DefaultAzureCredential` and `EnvironmentCredential` can be configured with
environment variables. Each type of authentication requires values for specific
variables:

#### Service principal with secret
>|variable name|value
>|-|-
>|`AZURE_CLIENT_ID`|service principal's app id
>|`AZURE_TENANT_ID`|id of the principal's Azure Active Directory tenant
>|`AZURE_CLIENT_SECRET`|one of the service principal's client secrets

#### Service principal with certificate
>|variable name|value
>|-|-
>|`AZURE_CLIENT_ID`|service principal's app id
>|`AZURE_TENANT_ID`|id of the principal's Azure Active Directory tenant
>|`AZURE_CLIENT_CERTIFICATE_PATH`|path to a PEM-encoded certificate file including private key (without password)

#### Username and password
>|variable name|value
>|-|-
>|`AZURE_CLIENT_ID`|id of an Azure Active Directory application
>|`AZURE_USERNAME`|a username (usually an email address)
>|`AZURE_PASSWORD`|that user's password

Configuration is attempted in the above order. For example, if both
`AZURE_CLIENT_SECRET` and `AZURE_CLIENT_CERTIFICATE_PATH` have values,
`AZURE_CLIENT_SECRET` will be used.

# Examples
## Authenticating with `DefaultAzureCredential`
Expand Down Expand Up @@ -173,9 +194,9 @@ client = EventHubClient(host, event_hub_path, credential)
```

## Async credentials:
This library includes a complete async API supported on Python 3.5+. To use the
async credentials in `azure.identity.aio`, you must first install an async
transport, such as [`aiohttp`](https://pypi.org/project/aiohttp/). See
This library includes an async API supported on Python 3.5+. To use the async
credentials in `azure.identity.aio`, you must first install an async transport,
such as [`aiohttp`](https://pypi.org/project/aiohttp/). See
[azure-core documentation](https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/core/azure-core/README.md#transport)
for more information.

Expand Down
6 changes: 3 additions & 3 deletions sdk/identity/azure-identity/azure/identity/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
from .browser_auth import InteractiveBrowserCredential
from ._browser_auth import InteractiveBrowserCredential
from .credentials import (
CertificateCredential,
ChainedTokenCredential,
Expand All @@ -19,10 +19,10 @@ class DefaultAzureCredential(ChainedTokenCredential):
A default credential capable of handling most Azure SDK authentication scenarios.

When environment variable configuration is present, it authenticates as a service principal
using :class:`identity.EnvironmentCredential`.
using :class:`azure.identity.EnvironmentCredential`.

When environment configuration is not present, it authenticates with a managed identity
using :class:`identity.ManagedIdentityCredential`.
using :class:`azure.identity.ManagedIdentityCredential`.
"""

def __init__(self, **kwargs):
Expand Down
14 changes: 10 additions & 4 deletions sdk/identity/azure-identity/azure/identity/_authn_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
except ImportError:
TYPE_CHECKING = False
if TYPE_CHECKING:
# pylint:disable=unused-import
from time import struct_time
from typing import Any, Dict, Iterable, Mapping, Optional
from typing import Any, Dict, Iterable, Mapping, Optional, Union
from azure.core.pipeline import PipelineResponse
from azure.core.pipeline.policies import HTTPPolicy

Expand Down Expand Up @@ -60,13 +61,13 @@ def _deserialize_and_cache_token(self, response, scopes, request_time):
token = payload["access_token"]

# AccessToken wants expires_on as an int
expires_on = payload.get("expires_on") or int(payload["expires_in"]) + request_time
expires_on = payload.get("expires_on") or int(payload["expires_in"]) + request_time # type: Union[str, int]
try:
expires_on = int(expires_on)
except ValueError:
# probably an App Service MSI response, convert it to epoch seconds
try:
t = self._parse_app_service_expires_on(expires_on)
t = self._parse_app_service_expires_on(expires_on) # type: ignore
expires_on = calendar.timegm(t)
except ValueError:
# have a token but don't know when it expires -> treat it as single-use
Expand Down Expand Up @@ -120,7 +121,12 @@ class AuthnClient(AuthnClientBase):
def __init__(self, auth_url, config=None, policies=None, transport=None, **kwargs):
# type: (str, Optional[Configuration], Optional[Iterable[HTTPPolicy]], Optional[HttpTransport], Mapping[str, Any]) -> None
config = config or self._create_config(**kwargs)
policies = policies or [ContentDecodePolicy(), config.retry_policy, config.logging_policy, DistributedTracingPolicy()]
policies = policies or [
ContentDecodePolicy(),
config.retry_policy,
config.logging_policy,
DistributedTracingPolicy(),
]
if not transport:
transport = RequestsTransport(**kwargs)
self._pipeline = Pipeline(transport=transport, policies=policies)
Expand Down
2 changes: 1 addition & 1 deletion sdk/identity/azure-identity/azure/identity/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from cryptography.hazmat.backends import default_backend
from msal.oauth2cli import JwtSigner

from .constants import Endpoints
from ._constants import Endpoints

try:
from typing import TYPE_CHECKING
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ class InteractiveBrowserCredential(ConfidentialClientCredential):
https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code

:param str client_id: the application's client ID
:param str secret: one of the application's client secrets
:param str client_secret: one of the application's client secrets

**Keyword arguments:**
Keyword arguments
- *tenant (str)*: a tenant ID or a domain associated with a tenant. Defaults to the 'organizations' tenant,
which can authenticate work or school accounts.
- *timeout (int)*: seconds to wait for the user to complete authentication. Defaults to 300 (5 minutes).

*tenant (str)* - a tenant ID or a domain associated with a tenant. If not provided, the credential defaults to the
'organizations' tenant, which can authenticate work or school accounts.
*timeout (str)* - seconds to wait for the user to complete authentication. Defaults to 300 (5 minutes).
"""

def __init__(self, client_id, client_secret, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from azure.core.pipeline.policies import ContentDecodePolicy, HeadersPolicy, NetworkTraceLoggingPolicy, RetryPolicy

from ._authn_client import AuthnClient
from .constants import Endpoints, EnvironmentVariables
from ._constants import Endpoints, EnvironmentVariables


class _ManagedIdentityBase(object):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
VERSION = "1.0.0b1"
VERSION = "1.0.0b2"
4 changes: 2 additions & 2 deletions sdk/identity/azure-identity/azure/identity/aio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ class DefaultAzureCredential(ChainedTokenCredential):
A default credential capable of handling most Azure SDK authentication scenarios.

When environment variable configuration is present, it authenticates as a service principal
using :class:`identity.aio.EnvironmentCredential`.
using :class:`azure.identity.aio.EnvironmentCredential`.

When environment configuration is not present, it authenticates with a managed identity
using :class:`identity.aio.ManagedIdentityCredential`.
using :class:`azure.identity.aio.ManagedIdentityCredential`.
"""

def __init__(self, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from azure.core.pipeline.policies import ContentDecodePolicy, HeadersPolicy, NetworkTraceLoggingPolicy, AsyncRetryPolicy

from ._authn_client import AsyncAuthnClient
from ..constants import Endpoints, EnvironmentVariables
from .._constants import Endpoints, EnvironmentVariables
from .._managed_identity import _ManagedIdentityBase


Expand Down
30 changes: 18 additions & 12 deletions sdk/identity/azure-identity/azure/identity/aio/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
from ._authn_client import AsyncAuthnClient
from ._managed_identity import ImdsCredential, MsiCredential
from .._base import ClientSecretCredentialBase, CertificateCredentialBase
from ..constants import Endpoints, EnvironmentVariables
from ..credentials import ChainedTokenCredential
from .._constants import Endpoints, EnvironmentVariables
from ..credentials import ChainedTokenCredential as SyncChainedTokenCredential

# pylint:disable=too-few-public-methods

Expand Down Expand Up @@ -80,18 +80,24 @@ async def get_token(self, *scopes: str) -> AccessToken:

class EnvironmentCredential:
"""
Authenticates as a service principal using a client ID/secret pair or a certificate,
depending on environment variable settings.

These environment variables are required:
Authenticates as a service principal using a client secret or a certificate, or as a user with a username and
password, depending on environment variable settings. Configuration is attempted in this order, using these
environment variables:

Service principal with secret:
- **AZURE_CLIENT_ID**: the service principal's client ID
- **AZURE_CLIENT_SECRET**: one of the service principal's client secrets
- **AZURE_TENANT_ID**: ID of the service principal's tenant. Also called its 'directory' ID.

Additionally, set **one** of these to configure client secret or certificate authentication:

- **AZURE_CLIENT_SECRET**: one of the service principal's client secrets
Service principal with certificate:
- **AZURE_CLIENT_ID**: the service principal's client ID
- **AZURE_CLIENT_CERTIFICATE_PATH**: path to a PEM-encoded certificate file including the private key
- **AZURE_TENANT_ID**: ID of the service principal's tenant. Also called its 'directory' ID.

User with username and password:
- **AZURE_CLIENT_ID**: the application's client ID
- **AZURE_USERNAME**: a username (usually an email address)
- **AZURE_PASSWORD**: that user's password
"""

def __init__(self, **kwargs: Mapping[str, Any]) -> None:
Expand Down Expand Up @@ -156,7 +162,7 @@ async def get_token(self, *scopes: str) -> AccessToken:
return AccessToken()


class ChainedTokenCredential(ChainedTokenCredential):
class ChainedTokenCredential(SyncChainedTokenCredential):
"""
A sequence of credentials that is itself a credential. Its ``get_token`` method calls ``get_token`` on each
credential in the sequence, in order, returning the first valid token received.
Expand All @@ -165,7 +171,7 @@ class ChainedTokenCredential(ChainedTokenCredential):
:type credentials: :class:`azure.core.credentials.TokenCredential`
"""

async def get_token(self, *scopes: str) -> AccessToken: # type: ignore
async def get_token(self, *scopes: str) -> AccessToken:
"""
Asynchronously request a token from each credential, in order, returning the first token
received. If none provides a token, raises :class:`azure.core.exceptions.ClientAuthenticationError`
Expand All @@ -175,7 +181,7 @@ async def get_token(self, *scopes: str) -> AccessToken: # type: ignore
:raises: :class:`azure.core.exceptions.ClientAuthenticationError`
"""
history = []
for credential in self._credentials:
for credential in self.credentials:
try:
return await credential.get_token(*scopes)
except ClientAuthenticationError as ex:
Expand Down
Loading