Skip to content

Commit c1b88c2

Browse files
author
Rakshith Bhyravabhotla
authored
Eh named key (#18292)
1 parent 61971ae commit c1b88c2

14 files changed

+204
-22
lines changed

sdk/eventhub/azure-eventhub/CHANGELOG.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# Release History
22

3-
## 5.4.1 (Unreleased)
3+
## 5.5.0 (Unreleased)
4+
5+
**New Features**
6+
- Added support for using `azure.core.credentials.AzureNamedKeyCredential` as credential for authenticating producer and consumer clients.
7+
8+
**Notes**
9+
10+
- Updated azure-core dependency to 1.14.0.
411

512
**Bug Fixes**
613

sdk/eventhub/azure-eventhub/azure/eventhub/_client_base.py

+26-4
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020

2121
from uamqp import AMQPClient, Message, authentication, constants, errors, compat, utils
2222
import six
23+
from azure.core.credentials import AccessToken, AzureSasCredential, AzureNamedKeyCredential
2324
from azure.core.utils import parse_connection_string as core_parse_connection_string
24-
from azure.core.credentials import AccessToken, AzureSasCredential
25+
2526

2627
from .exceptions import _handle_exception, ClientClosedError, ConnectError
2728
from ._configuration import Configuration
@@ -173,6 +174,25 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
173174
raise ValueError("No token scope provided.")
174175
return _generate_sas_token(scopes[0], self.policy, self.key)
175176

177+
class EventhubAzureNamedKeyTokenCredential(object):
178+
"""The named key credential used for authentication.
179+
180+
:param credential: The AzureNamedKeyCredential that should be used.
181+
:type credential: ~azure.core.credentials.AzureNamedKeyCredential
182+
"""
183+
184+
def __init__(self, azure_named_key_credential):
185+
# type: (AzureNamedKeyCredential) -> None
186+
self._credential = azure_named_key_credential
187+
self.token_type = b"servicebus.windows.net:sastoken"
188+
189+
def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
190+
# type: (str, Any) -> _AccessToken
191+
if not scopes:
192+
raise ValueError("No token scope provided.")
193+
name, key = self._credential.named_key
194+
return _generate_sas_token(scopes[0], name, key)
195+
176196

177197
class EventHubSASTokenCredential(object):
178198
"""The shared access token credential used for authentication.
@@ -197,7 +217,7 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
197217
"""
198218
return AccessToken(self.token, self.expiry)
199219

200-
class AzureSasTokenCredential(object):
220+
class EventhubAzureSasTokenCredential(object):
201221
"""The shared access token credential used for authentication
202222
when AzureSasCredential is provided.
203223
@@ -226,15 +246,17 @@ def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
226246

227247
class ClientBase(object): # pylint:disable=too-many-instance-attributes
228248
def __init__(self, fully_qualified_namespace, eventhub_name, credential, **kwargs):
229-
# type: (str, str, Union[AzureSasCredential, TokenCredential], Any) -> None
249+
# type: (str, str, Union[AzureSasCredential, TokenCredential, AzureNamedKeyCredential], Any) -> None
230250
self.eventhub_name = eventhub_name
231251
if not eventhub_name:
232252
raise ValueError("The eventhub name can not be None or empty.")
233253
path = "/" + eventhub_name if eventhub_name else ""
234254
self._address = _Address(hostname=fully_qualified_namespace, path=path)
235255
self._container_id = CONTAINER_PREFIX + str(uuid.uuid4())[:8]
236256
if isinstance(credential, AzureSasCredential):
237-
self._credential = AzureSasTokenCredential(credential)
257+
self._credential = EventhubAzureSasTokenCredential(credential)
258+
elif isinstance(credential, AzureNamedKeyCredential):
259+
self._credential = EventhubAzureNamedKeyTokenCredential(credential) # type: ignore
238260
else:
239261
self._credential = credential #type: ignore
240262
self._keep_alive = kwargs.get("keep_alive", 30)

sdk/eventhub/azure-eventhub/azure/eventhub/_consumer_client.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
if TYPE_CHECKING:
1717
import datetime
18-
from azure.core.credentials import TokenCredential, AzureSasCredential
18+
from azure.core.credentials import TokenCredential, AzureSasCredential, AzureNamedKeyCredential
1919
from typing import ( # pylint: disable=ungrouped-imports
2020
Any,
2121
Union,
@@ -59,6 +59,7 @@ class EventHubConsumerClient(ClientBase):
5959
:class:`EventHubSharedKeyCredential<azure.eventhub.EventHubSharedKeyCredential>`, or credential objects generated
6060
by the azure-identity library and objects that implement the `get_token(self, *scopes)` method.
6161
:type credential: ~azure.core.credentials.TokenCredential or ~azure.core.credentials.AzureSasCredential
62+
or ~azure.core.credentials.AzureNamedKeyCredential
6263
:keyword bool logging_enable: Whether to output network trace logs to the logger. Default is `False`.
6364
:keyword float auth_timeout: The time in seconds to wait for a token to be authorized by the service.
6465
The default value is 60 seconds. If set to 0, no timeout will be enforced from the client.
@@ -129,7 +130,7 @@ def __init__(
129130
fully_qualified_namespace, # type: str
130131
eventhub_name, # type: str
131132
consumer_group, # type: str
132-
credential, # type: Union[AzureSasCredential, TokenCredential]
133+
credential, # type: Union[AzureSasCredential, TokenCredential, AzureNamedKeyCredential]
133134
**kwargs # type: Any
134135
):
135136
# type: (...) -> None

sdk/eventhub/azure-eventhub/azure/eventhub/_producer_client.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from ._common import EventDataBatch, EventData
1717

1818
if TYPE_CHECKING:
19-
from azure.core.credentials import TokenCredential, AzureSasCredential
19+
from azure.core.credentials import TokenCredential, AzureSasCredential, AzureNamedKeyCredential
2020

2121
_LOGGER = logging.getLogger(__name__)
2222

@@ -33,6 +33,7 @@ class EventHubProducerClient(ClientBase):
3333
:class:`EventHubSharedKeyCredential<azure.eventhub.EventHubSharedKeyCredential>`, or credential objects generated
3434
by the azure-identity library and objects that implement the `get_token(self, *scopes)` method.
3535
:type credential: ~azure.core.credentials.TokenCredential or ~azure.core.credentials.AzureSasCredential
36+
or ~azure.core.credentials.AzureNamedKeyCredential
3637
:keyword bool logging_enable: Whether to output network trace logs to the logger. Default is `False`.
3738
:keyword float auth_timeout: The time in seconds to wait for a token to be authorized by the service.
3839
The default value is 60 seconds. If set to 0, no timeout will be enforced from the client.
@@ -74,7 +75,7 @@ def __init__(
7475
self,
7576
fully_qualified_namespace, # type: str
7677
eventhub_name, # type: str
77-
credential, # type: Union[AzureSasCredential, TokenCredential]
78+
credential, # type: Union[AzureSasCredential, TokenCredential, AzureNamedKeyCredential]
7879
**kwargs # type: Any
7980
):
8081
# type:(...) -> None

sdk/eventhub/azure-eventhub/azure/eventhub/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
# Licensed under the MIT License.
44
# ------------------------------------
55

6-
VERSION = "5.4.1"
6+
VERSION = "5.5.0"

sdk/eventhub/azure-eventhub/azure/eventhub/aio/_client_base_async.py

+25-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
Message,
2020
AMQPClientAsync,
2121
)
22-
from azure.core.credentials import AccessToken, AzureSasCredential
22+
from azure.core.credentials import AccessToken, AzureSasCredential, AzureNamedKeyCredential
2323

2424
from .._client_base import ClientBase, _generate_sas_token, _parse_conn_str
2525
from .._utils import utc_from_timestamp, parse_sas_credential
@@ -83,7 +83,26 @@ async def get_token(self, *scopes: str, **kwargs: Any) -> AccessToken: # pylint
8383
"""
8484
return AccessToken(self.token, self.expiry)
8585

86-
class AzureSasTokenCredentialAsync(object):
86+
class EventhubAzureNamedKeyTokenCredentialAsync(object):
87+
"""The named key credential used for authentication.
88+
89+
:param credential: The AzureNamedKeyCredential that should be used.
90+
:type credential: ~azure.core.credentials.AzureNamedKeyCredential
91+
"""
92+
93+
def __init__(self, azure_named_key_credential):
94+
# type: (AzureNamedKeyCredential) -> None
95+
self._credential = azure_named_key_credential
96+
self.token_type = b"servicebus.windows.net:sastoken"
97+
98+
async def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
99+
if not scopes:
100+
raise ValueError("No token scope provided.")
101+
name, key = self._credential.named_key
102+
return _generate_sas_token(scopes[0], name, key)
103+
104+
105+
class EventhubAzureSasTokenCredentialAsync(object):
87106
"""The shared access token credential used for authentication
88107
when AzureSasCredential is provided.
89108
@@ -107,12 +126,14 @@ def __init__(
107126
self,
108127
fully_qualified_namespace: str,
109128
eventhub_name: str,
110-
credential: Union["AsyncTokenCredential", AzureSasCredential],
129+
credential: Union["AsyncTokenCredential", AzureSasCredential, AzureNamedKeyCredential],
111130
**kwargs: Any
112131
) -> None:
113132
self._loop = kwargs.pop("loop", None)
114133
if isinstance(credential, AzureSasCredential):
115-
self._credential = AzureSasTokenCredentialAsync(credential) # type: ignore
134+
self._credential = EventhubAzureSasTokenCredentialAsync(credential) # type: ignore
135+
elif isinstance(credential, AzureNamedKeyCredential):
136+
self._credential = EventhubAzureNamedKeyTokenCredentialAsync(credential) # type: ignore
116137
else:
117138
self._credential = credential # type: ignore
118139
super(ClientBaseAsync, self).__init__(

sdk/eventhub/azure-eventhub/azure/eventhub/aio/_consumer_client_async.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
Awaitable,
1919
)
2020

21-
from azure.core.credentials import AzureSasCredential
21+
from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential
2222

2323
from ._eventprocessor.event_processor import EventProcessor
2424
from ._consumer_async import EventHubConsumer
@@ -66,6 +66,7 @@ class EventHubConsumerClient(ClientBaseAsync):
6666
:class:`EventHubSharedKeyCredential<azure.eventhub.aio.EventHubSharedKeyCredential>`, or credential objects
6767
generated by the azure-identity library and objects that implement the `get_token(self, *scopes)` method.
6868
:type credential: ~azure.core.credentials_async.AsyncTokenCredential or ~azure.core.credentials.AzureSasCredential
69+
or ~azure.core.credentials.AzureNamedKeyCredential
6970
:keyword bool logging_enable: Whether to output network trace logs to the logger. Default is `False`.
7071
:keyword float auth_timeout: The time in seconds to wait for a token to be authorized by the service.
7172
The default value is 60 seconds. If set to 0, no timeout will be enforced from the client.
@@ -136,7 +137,7 @@ def __init__(
136137
fully_qualified_namespace: str,
137138
eventhub_name: str,
138139
consumer_group: str,
139-
credential: Union["AsyncTokenCredential", AzureSasCredential],
140+
credential: Union["AsyncTokenCredential", AzureSasCredential, AzureNamedKeyCredential],
140141
**kwargs
141142
) -> None:
142143
self._checkpoint_store = kwargs.pop("checkpoint_store", None)

sdk/eventhub/azure-eventhub/azure/eventhub/aio/_producer_client_async.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Any, Union, TYPE_CHECKING, List, Optional, Dict, cast
99
from uamqp import constants
1010

11-
from azure.core.credentials import AzureSasCredential
11+
from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential
1212

1313
from ..exceptions import ConnectError, EventHubError
1414
from ._client_base_async import ClientBaseAsync
@@ -36,6 +36,7 @@ class EventHubProducerClient(ClientBaseAsync):
3636
:class:`EventHubSharedKeyCredential<azure.eventhub.aio.EventHubSharedKeyCredential>`, or credential objects
3737
generated by the azure-identity library and objects that implement the `get_token(self, *scopes)` method.
3838
:type credential: ~azure.core.credentials_async.AsyncTokenCredential or ~azure.core.credentials.AzureSasCredential
39+
or ~azure.core.credentials.AzureNamedKeyCredential
3940
:keyword bool logging_enable: Whether to output network trace logs to the logger. Default is `False`.
4041
:keyword float auth_timeout: The time in seconds to wait for a token to be authorized by the service.
4142
The default value is 60 seconds. If set to 0, no timeout will be enforced from the client.
@@ -76,7 +77,7 @@ def __init__(
7677
self,
7778
fully_qualified_namespace: str,
7879
eventhub_name: str,
79-
credential: Union["AsyncTokenCredential", AzureSasCredential],
80+
credential: Union["AsyncTokenCredential", AzureSasCredential, AzureNamedKeyCredential],
8081
**kwargs
8182
) -> None:
8283
super(EventHubProducerClient, self).__init__(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env python
2+
3+
# --------------------------------------------------------------------------------------------
4+
# Copyright (c) Microsoft Corporation. All rights reserved.
5+
# Licensed under the MIT License. See License.txt in the project root for license information.
6+
# --------------------------------------------------------------------------------------------
7+
8+
"""
9+
Example to demonstrate utilizing AzureNamedKeyCredential to authenticate with Event Hubs asynchronously.
10+
"""
11+
12+
# pylint: disable=C0111
13+
14+
import os
15+
import asyncio
16+
import time
17+
from azure.core.credentials import AzureNamedKeyCredential
18+
from azure.eventhub.aio import EventHubProducerClient
19+
from azure.eventhub import EventData
20+
21+
# Target namespace and hub must also be specified.
22+
FULLY_QUALIFIED_NAMESPACE = os.environ['EVENT_HUB_HOSTNAME']
23+
EVENTHUB_NAME = os.environ['EVENT_HUB_NAME']
24+
25+
EVENTHUB_POLICY_NAME = os.environ['EVENT_HUB_SAS_POLICY']
26+
EVENTHUB_KEY = os.environ['EVENT_HUB_SAS_KEY']
27+
28+
credential = AzureNamedKeyCredential(EVENTHUB_POLICY_NAME, EVENTHUB_KEY)
29+
30+
producer_client = EventHubProducerClient(
31+
fully_qualified_namespace=FULLY_QUALIFIED_NAMESPACE,
32+
eventhub_name=EVENTHUB_NAME,
33+
credential=credential,
34+
logging_enable=True
35+
)
36+
37+
start_time = time.time()
38+
async def authenticate_with_named_key():
39+
async with producer_client:
40+
event_data_batch = await producer_client.create_batch()
41+
event_data_batch.add(EventData('Single message'))
42+
await producer_client.send_batch(event_data_batch)
43+
44+
loop = asyncio.get_event_loop()
45+
start_time = time.time()
46+
loop.run_until_complete(authenticate_with_named_key())
47+
print("Send messages in {} seconds.".format(time.time() - start_time))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env python
2+
3+
# --------------------------------------------------------------------------------------------
4+
# Copyright (c) Microsoft Corporation. All rights reserved.
5+
# Licensed under the MIT License. See License.txt in the project root for license information.
6+
# --------------------------------------------------------------------------------------------
7+
8+
"""
9+
Example to demonstrate utilizing AzureNamedKeyCredential to authenticate with Event Hubs.
10+
"""
11+
12+
# pylint: disable=C0111
13+
14+
import os
15+
import time
16+
from azure.core.credentials import AzureNamedKeyCredential
17+
from azure.eventhub import EventHubProducerClient, EventData
18+
19+
# Target namespace and hub must also be specified.
20+
FULLY_QUALIFIED_NAMESPACE = os.environ['EVENT_HUB_HOSTNAME']
21+
EVENTHUB_NAME = os.environ['EVENT_HUB_NAME']
22+
23+
EVENTHUB_POLICY_NAME = os.environ['EVENT_HUB_SAS_POLICY']
24+
EVENTHUB_KEY = os.environ['EVENT_HUB_SAS_KEY']
25+
26+
credential = AzureNamedKeyCredential(EVENTHUB_POLICY_NAME, EVENTHUB_KEY)
27+
28+
producer_client = EventHubProducerClient(
29+
fully_qualified_namespace=FULLY_QUALIFIED_NAMESPACE,
30+
eventhub_name=EVENTHUB_NAME,
31+
credential=credential,
32+
logging_enable=True
33+
)
34+
35+
start_time = time.time()
36+
with producer_client:
37+
event_data_batch = producer_client.create_batch()
38+
event_data_batch.add(EventData('Single message'))
39+
producer_client.send_batch(event_data_batch)
40+
41+
print("Send messages in {} seconds.".format(time.time() - start_time))

sdk/eventhub/azure-eventhub/setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
zip_safe=False,
7070
packages=find_packages(exclude=exclude_packages),
7171
install_requires=[
72-
"azure-core<2.0.0,>=1.13.0",
72+
"azure-core<2.0.0,>=1.14.0",
7373
"uamqp>=1.3.0,<2.0.0",
7474
],
7575
extras_require={

sdk/eventhub/azure-eventhub/tests/livetest/asynctests/test_auth_async.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import datetime
1010
import time
1111

12-
from azure.core.credentials import AzureSasCredential
12+
from azure.core.credentials import AzureSasCredential, AzureNamedKeyCredential
1313
from azure.identity.aio import EnvironmentCredential
1414
from azure.eventhub import EventData
1515
from azure.eventhub.aio import EventHubConsumerClient, EventHubProducerClient, EventHubSharedKeyCredential
@@ -134,3 +134,24 @@ async def test_client_azure_sas_credential_async(self,
134134
batch = await producer_client.create_batch(partition_id='0')
135135
batch.add(EventData(body='A single message'))
136136
await producer_client.send_batch(batch)
137+
138+
@pytest.mark.liveTest
139+
@pytest.mark.asyncio
140+
async def test_client_azure_named_key_credential_async(live_eventhub):
141+
142+
credential = AzureNamedKeyCredential(live_eventhub['key_name'], live_eventhub['access_key'])
143+
consumer_client = EventHubConsumerClient(fully_qualified_namespace=live_eventhub['hostname'],
144+
eventhub_name=live_eventhub['event_hub'],
145+
consumer_group='$default',
146+
credential=credential,
147+
user_agent='customized information')
148+
149+
assert (await consumer_client.get_eventhub_properties()) is not None
150+
151+
credential.update("foo", "bar")
152+
153+
with pytest.raises(Exception):
154+
await consumer_client.get_eventhub_properties()
155+
156+
credential.update(live_eventhub['key_name'], live_eventhub['access_key'])
157+
assert (await consumer_client.get_eventhub_properties()) is not None

0 commit comments

Comments
 (0)