Skip to content

Commit ee8ff2c

Browse files
authored
Key Vault clients use azure-identity credentials (#5694)
1 parent b82d567 commit ee8ff2c

21 files changed

+443
-467
lines changed

sdk/keyvault/azure-security-keyvault/azure/security/keyvault/__init__.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
# Licensed under the MIT License. See LICENSE.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6+
from .keys._client import KeyClient
7+
from .secrets._client import SecretClient
68
from .vault_client import VaultClient
79
from .version import VERSION
810

9-
__all__ = ["VaultClient"]
11+
__all__ = ["VaultClient", "KeyClient", "SecretClient"]
1012

1113
__version__ = VERSION

sdk/keyvault/azure-security-keyvault/azure/security/keyvault/_internal.py

+79-10
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,34 @@
33
# Licensed under the MIT License. See LICENSE.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6-
from azure.core.pipeline.policies import HTTPPolicy
6+
from collections import namedtuple
7+
from typing import TYPE_CHECKING
8+
9+
if TYPE_CHECKING:
10+
# pylint:disable=unused-import
11+
from typing import Any, Mapping, Optional
12+
from azure.core.credentials import TokenCredential
13+
from azure.core.pipeline.transport import HttpTransport
714

815
try:
916
import urllib.parse as parse
1017
except ImportError:
1118
import urlparse as parse # pylint: disable=import-error
1219

13-
from collections import namedtuple
20+
from azure.core import Configuration
21+
from azure.core.pipeline import Pipeline
22+
from azure.core.pipeline.policies import BearerTokenCredentialPolicy, HTTPPolicy
23+
from azure.core.pipeline.transport import RequestsTransport
24+
25+
from ._generated import KeyVaultClient
1426

1527

1628
_VaultId = namedtuple("VaultId", ["vault_url", "collection", "name", "version"])
1729

1830

31+
KEY_VAULT_SCOPES = ("https://vault.azure.net/.default",)
32+
33+
1934
def _parse_vault_id(url):
2035
try:
2136
parsed_uri = parse.urlparse(url)
@@ -37,13 +52,67 @@ def _parse_vault_id(url):
3752
)
3853

3954

40-
# TODO: integrate with azure.core
41-
class _BearerTokenCredentialPolicy(HTTPPolicy):
42-
def __init__(self, credentials):
43-
self._credentials = credentials
55+
class _KeyVaultClientBase(object):
56+
"""
57+
:param credential: A credential or credential provider which can be used to authenticate to the vault,
58+
a ValueError will be raised if the entity is not provided
59+
:type credential: azure.core.credentials.TokenCredential
60+
:param str vault_url: The url of the vault to which the client will connect,
61+
a ValueError will be raised if the entity is not provided
62+
:param ~azure.core.configuration.Configuration config: The configuration for the KeyClient
63+
"""
64+
65+
@staticmethod
66+
def create_config(credential, api_version=None, **kwargs):
67+
# type: (TokenCredential, Optional[str], Mapping[str, Any]) -> Configuration
68+
if api_version is None:
69+
api_version = KeyVaultClient.DEFAULT_API_VERSION
70+
config = KeyVaultClient.get_configuration_class(api_version, aio=False)(credential, **kwargs)
71+
config.authentication_policy = BearerTokenCredentialPolicy(credential, scopes=KEY_VAULT_SCOPES)
72+
return config
73+
74+
def __init__(self, vault_url, credential, config=None, transport=None, api_version=None, **kwargs):
75+
# type: (str, TokenCredential, Configuration, Optional[HttpTransport], Optional[str], **Any) -> None
76+
if not credential:
77+
raise ValueError(
78+
"credential should be an object supporting the TokenCredential protocol, such as a credential from azure-identity"
79+
)
80+
if not vault_url:
81+
raise ValueError("vault_url must be the URL of an Azure Key Vault")
82+
83+
self._vault_url = vault_url.strip(" /")
84+
85+
client = kwargs.pop("generated_client", None)
86+
if client:
87+
# caller provided a configured client -> nothing left to initialize
88+
self._client = client
89+
return
90+
91+
if api_version is None:
92+
api_version = KeyVaultClient.DEFAULT_API_VERSION
93+
94+
config = config or self.create_config(credential, api_version=api_version, **kwargs)
95+
pipeline = kwargs.pop("pipeline", None) or self._build_pipeline(config, transport)
96+
self._client = KeyVaultClient(credential, api_version=api_version, pipeline=pipeline, aio=False, **kwargs)
97+
98+
def _build_pipeline(self, config, transport):
99+
# type: (Configuration, HttpTransport) -> Pipeline
100+
policies = [
101+
config.headers_policy,
102+
config.user_agent_policy,
103+
config.proxy_policy,
104+
config.redirect_policy,
105+
config.retry_policy,
106+
config.authentication_policy,
107+
config.logging_policy,
108+
]
109+
110+
if transport is None:
111+
transport = RequestsTransport(config)
44112

45-
def send(self, request, **kwargs):
46-
auth_header = "Bearer " + self._credentials.token["access_token"]
47-
request.http_request.headers["Authorization"] = auth_header
113+
return Pipeline(transport, policies=policies)
48114

49-
return self.next.send(request, **kwargs)
115+
@property
116+
def vault_url(self):
117+
# type: () -> str
118+
return self._vault_url

sdk/keyvault/azure-security-keyvault/azure/security/keyvault/aio/_internal.py

+91-4
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,112 @@
33
# Licensed under the MIT License. See LICENSE.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6-
from typing import Any, Callable
6+
from typing import Any, Callable, Mapping, Optional, TYPE_CHECKING
7+
8+
if TYPE_CHECKING:
9+
try:
10+
from azure.core.credentials import TokenCredential
11+
except ImportError:
12+
# TokenCredential is a typing_extensions.Protocol; we don't depend on that package
13+
pass
714

815
from azure.core.async_paging import AsyncPagedMixin
16+
from azure.core.configuration import Configuration
17+
from azure.core.pipeline import AsyncPipeline
18+
from azure.core.pipeline.policies import AsyncBearerTokenCredentialPolicy
19+
from azure.core.pipeline.transport import AsyncioRequestsTransport, HttpTransport
920
from msrest.serialization import Model
1021

22+
from .._generated import KeyVaultClient
23+
from .._internal import KEY_VAULT_SCOPES
24+
1125

1226
class AsyncPagingAdapter:
1327
"""For each item in an AsyncPagedMixin, returns the result of applying fn to that item.
1428
Python 3.6 added syntax that could replace this (yield within async for)."""
15-
def __init__(self, pages, fn):
16-
# type: (AsyncPagedMixin, Callable[[Model], Any]) -> None
29+
30+
def __init__(self, pages: AsyncPagedMixin, fn: Callable[[Model], Any]) -> None:
1731
self._pages = pages
1832
self._fn = fn
1933

2034
def __aiter__(self):
2135
return self
2236

23-
async def __anext__(self):
37+
async def __anext__(self) -> Any:
2438
item = await self._pages.__anext__()
2539
if not item:
2640
raise StopAsyncIteration
2741
return self._fn(item)
42+
43+
44+
class _AsyncKeyVaultClientBase:
45+
"""
46+
:param credential: A credential or credential provider which can be used to authenticate to the vault,
47+
a ValueError will be raised if the entity is not provided
48+
:type credential: azure.authentication.Credential or azure.authentication.CredentialProvider
49+
:param str vault_url: The url of the vault to which the client will connect,
50+
a ValueError will be raised if the entity is not provided
51+
:param ~azure.core.configuration.Configuration config: The configuration for the SecretClient
52+
"""
53+
54+
@staticmethod
55+
def create_config(
56+
credential: "TokenCredential", api_version: str = None, **kwargs: Mapping[str, Any]
57+
) -> Configuration:
58+
if api_version is None:
59+
api_version = KeyVaultClient.DEFAULT_API_VERSION
60+
config = KeyVaultClient.get_configuration_class(api_version, aio=True)(credential, **kwargs)
61+
config.authentication_policy = AsyncBearerTokenCredentialPolicy(credential, scopes=KEY_VAULT_SCOPES)
62+
return config
63+
64+
def __init__(
65+
self,
66+
vault_url: str,
67+
credential: "TokenCredential",
68+
config: Configuration = None,
69+
transport: HttpTransport = None,
70+
api_version: str = None,
71+
**kwargs: Any
72+
) -> None:
73+
if not credential:
74+
raise ValueError(
75+
"credential should be an object supporting the TokenCredential protocol, such as a credential from azure-identity"
76+
)
77+
if not vault_url:
78+
raise ValueError("vault_url must be the URL of an Azure Key Vault")
79+
80+
self._vault_url = vault_url.strip(" /")
81+
82+
client = kwargs.pop("generated_client", None)
83+
if client:
84+
# caller provided a configured client -> nothing left to initialize
85+
self._client = client
86+
return
87+
88+
if api_version is None:
89+
api_version = KeyVaultClient.DEFAULT_API_VERSION
90+
91+
config = config or self.create_config(credential, api_version=api_version, **kwargs)
92+
pipeline = kwargs.pop("pipeline", None) or self._build_pipeline(config, transport=transport)
93+
self._client = KeyVaultClient(credential, api_version=api_version, pipeline=pipeline, aio=True)
94+
95+
@staticmethod
96+
def _build_pipeline(config: Configuration, transport: HttpTransport) -> AsyncPipeline:
97+
policies = [
98+
config.headers_policy,
99+
config.user_agent_policy,
100+
config.proxy_policy,
101+
config.redirect_policy,
102+
config.retry_policy,
103+
config.authentication_policy,
104+
config.logging_policy,
105+
]
106+
107+
if transport is None:
108+
transport = AsyncioRequestsTransport(config)
109+
110+
return AsyncPipeline(transport, policies=policies)
111+
112+
@property
113+
def vault_url(self) -> str:
114+
return self._vault_url

0 commit comments

Comments
 (0)