-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Add AAD support for EG #19421
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
Add AAD support for EG #19421
Changes from 9 commits
3722b52
31e4478
ca3f361
b73b0f7
af4a5e7
7f03c04
cb12665
e7f9405
562faeb
55bd8aa
f21370d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,7 @@ | |
| # Changes may cause incorrect behavior and will be lost if the code is regenerated. | ||
| # -------------------------------------------------------------------------- | ||
|
|
||
| from typing import Any, Union, List, Dict, cast | ||
| from typing import Any, Union, List, Dict, TYPE_CHECKING, cast | ||
| from azure.core.credentials import AzureKeyCredential, AzureSasCredential | ||
| from azure.core.tracing.decorator_async import distributed_trace_async | ||
| from azure.core.messaging import CloudEvent | ||
|
|
@@ -22,20 +22,24 @@ | |
| DistributedTracingPolicy, | ||
| HttpLoggingPolicy, | ||
| UserAgentPolicy, | ||
| AsyncBearerTokenCredentialPolicy | ||
| ) | ||
| from .._policies import CloudEventDistributedTracingPolicy | ||
| from .._models import EventGridEvent | ||
| from .._helpers import ( | ||
| _get_authentication_policy, | ||
| _is_cloud_event, | ||
| _is_eventgrid_event, | ||
| _eventgrid_data_typecheck, | ||
| _build_request, | ||
| _cloud_event_to_generated, | ||
| _get_authentication_policy | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: missing trailing comma - did you run this through black?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nope - i did not - can do that EDIT: done |
||
| ) | ||
| from .._generated.aio import EventGridPublisherClient as EventGridPublisherClientAsync | ||
| from .._version import VERSION | ||
|
|
||
| if TYPE_CHECKING: | ||
| from azure.core.credentials_async import AsyncTokenCredential | ||
|
|
||
| SendType = Union[ | ||
| CloudEvent, EventGridEvent, Dict, List[CloudEvent], List[EventGridEvent], List[Dict] | ||
| ] | ||
|
|
@@ -49,8 +53,9 @@ class EventGridPublisherClient: | |
|
|
||
| :param str endpoint: The topic endpoint to send the events to. | ||
| :param credential: The credential object used for authentication which implements | ||
| SAS key authentication or SAS token authentication. | ||
| :type credential: ~azure.core.credentials.AzureKeyCredential or ~azure.core.credentials.AzureSasCredential | ||
| SAS key authentication or SAS token authentication or an AsyncTokenCredential. | ||
| :type credential: ~azure.core.credentials.AzureKeyCredential or ~azure.core.credentials.AzureSasCredential or | ||
| ~azure.core.credentials_async.AsyncTokenCredential | ||
| :rtype: None | ||
|
|
||
| .. admonition:: Example: | ||
|
|
@@ -73,7 +78,7 @@ class EventGridPublisherClient: | |
| def __init__( | ||
| self, | ||
| endpoint: str, | ||
| credential: Union[AzureKeyCredential, AzureSasCredential], | ||
| credential: Union["AsyncTokenCredential", AzureKeyCredential, AzureSasCredential], | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That seems inconsistent, if not used those types should be in the |
||
| **kwargs: Any | ||
| ) -> None: | ||
| self._client = EventGridPublisherClientAsync( | ||
|
|
@@ -83,9 +88,9 @@ def __init__( | |
|
|
||
| @staticmethod | ||
| def _policies( | ||
| credential: Union[AzureKeyCredential, AzureSasCredential], **kwargs: Any | ||
| credential: Union[AzureKeyCredential, AzureSasCredential, "AsyncTokenCredential"], **kwargs: Any | ||
| ) -> List[Any]: | ||
| auth_policy = _get_authentication_policy(credential) | ||
| auth_policy = _get_authentication_policy(credential, AsyncBearerTokenCredentialPolicy) | ||
| sdk_moniker = "eventgridpublisherclient/{}".format(VERSION) | ||
| policies = [ | ||
| RequestIdPolicy(**kwargs), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
|
|
||
| # coding: utf-8 | ||
| # ------------------------------------------------------------------------- | ||
| # Copyright (c) Microsoft Corporation. All rights reserved. | ||
| # Licensed under the MIT License. See License.txt in the project root for | ||
| # license information. | ||
| # -------------------------------------------------------------------------- | ||
| import asyncio | ||
| import os | ||
| import functools | ||
| from azure_devtools.scenario_tests.utilities import trim_kwargs_from_test_function | ||
| from azure.core.credentials import AccessToken | ||
| from eventgrid_preparer import EventGridTest | ||
|
|
||
|
|
||
| class AsyncFakeTokenCredential(object): | ||
| """Protocol for classes able to provide OAuth tokens. | ||
| :param str scopes: Lets you specify the type of access needed. | ||
| """ | ||
| def __init__(self): | ||
| self.token = AccessToken("YOU SHALL NOT PASS", 0) | ||
|
|
||
| async def get_token(self, *args): | ||
| return self.token | ||
|
|
||
|
|
||
| class AsyncEventGridTest(EventGridTest): | ||
|
|
||
| def generate_oauth_token(self): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You shouldn't need that, there is everything you need in devtools to care care of that for free |
||
| if self.is_live: | ||
| from azure.identity.aio import ClientSecretCredential | ||
| return ClientSecretCredential( | ||
| os.getenv("AZURE_TENANT_ID"), | ||
| os.getenv("AZURE_CLIENT_ID"), | ||
| os.getenv("AZURE_CLIENT_SECRET"), | ||
| ) | ||
| return self.generate_fake_token() | ||
|
|
||
| def generate_fake_token(self): | ||
| return AsyncFakeTokenCredential() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,12 +3,14 @@ | |
| import os | ||
| from collections import namedtuple | ||
|
|
||
| from azure_devtools.scenario_tests import ReplayableTest | ||
| from azure.core.credentials import AccessToken | ||
| from azure.mgmt.eventgrid import EventGridManagementClient | ||
| from azure.mgmt.eventgrid.models import Topic, InputSchema, JsonInputSchemaMapping, JsonField, JsonFieldWithDefault | ||
| from azure_devtools.scenario_tests.exceptions import AzureTestError | ||
|
|
||
| from devtools_testutils import ( | ||
| ResourceGroupPreparer, AzureMgmtPreparer, FakeResource | ||
| ResourceGroupPreparer, AzureMgmtPreparer, FakeResource, AzureMgmtTestCase | ||
| ) | ||
|
|
||
| from devtools_testutils.resource_testcase import RESOURCE_GROUP_PARAM | ||
|
|
@@ -25,6 +27,15 @@ | |
| DATA_VERSION_JSON_FIELD_WITH_DEFAULT = JsonFieldWithDefault(source_field='customDataVersion', default_value='') | ||
| CUSTOM_JSON_INPUT_SCHEMA_MAPPING = JsonInputSchemaMapping(id=ID_JSON_FIELD, topic=TOPIC_JSON_FIELD, event_time=EVENT_TIME_JSON_FIELD, event_type=EVENT_TYPE_JSON_FIELD_WITH_DEFAULT, subject=SUBJECT_JSON_FIELD_WITH_DEFAULT, data_version=DATA_VERSION_JSON_FIELD_WITH_DEFAULT) | ||
|
|
||
| class FakeTokenCredential(object): | ||
| """Protocol for classes able to provide OAuth tokens. | ||
| :param str scopes: Lets you specify the type of access needed. | ||
| """ | ||
| def __init__(self): | ||
| self.token = AccessToken("YOU SHALL NOT PASS", 0) | ||
|
|
||
| def get_token(self, *args): | ||
| return self.token | ||
|
|
||
| class EventGridTopicPreparer(AzureMgmtPreparer): | ||
| def __init__(self, | ||
|
|
@@ -94,4 +105,28 @@ def _get_resource_group(self, **kwargs): | |
| 'decorator @{} in front of this event grid topic preparer.' | ||
| raise AzureTestError(template.format(ResourceGroupPreparer.__name__)) | ||
|
|
||
|
|
||
| CachedEventGridTopicPreparer = functools.partial(EventGridTopicPreparer, use_cache=True) | ||
|
|
||
|
|
||
| class EventGridTest(AzureMgmtTestCase): | ||
| FILTER_HEADERS = ReplayableTest.FILTER_HEADERS + ['aeg-sas-key', 'aeg-sas-token'] | ||
|
|
||
| def __init__(self, method_name): | ||
| super(EventGridTest, self).__init__(method_name) | ||
|
|
||
| def get_oauth_endpoint(self): | ||
| return os.getenv("EG_TOPIC_HOSTNAME") | ||
|
|
||
| def generate_oauth_token(self): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same, you don't need that |
||
| if self.is_live: | ||
| from azure.identity import ClientSecretCredential | ||
| return ClientSecretCredential( | ||
| os.getenv("AZURE_TENANT_ID"), | ||
| os.getenv("AZURE_CLIENT_ID"), | ||
| os.getenv("AZURE_CLIENT_SECRET"), | ||
| ) | ||
| return self.generate_fake_token() | ||
|
|
||
| def generate_fake_token(self): | ||
| return FakeTokenCredential() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,10 +29,11 @@ | |
| from azure.eventgrid._helpers import _cloud_event_to_generated | ||
|
|
||
| from eventgrid_preparer import ( | ||
| CachedEventGridTopicPreparer | ||
| CachedEventGridTopicPreparer, | ||
| EventGridTest | ||
| ) | ||
|
|
||
| class EventGridPublisherClientTests(AzureMgmtTestCase): | ||
| class EventGridPublisherClientTests(EventGridTest): | ||
| FILTER_HEADERS = ReplayableTest.FILTER_HEADERS + ['aeg-sas-key', 'aeg-sas-token'] | ||
|
|
||
| @CachedResourceGroupPreparer(name_prefix='eventgridtest') | ||
|
|
@@ -343,5 +344,19 @@ def test_send_custom_schema_event_as_list(self, resource_group, eventgrid_topic, | |
|
|
||
| def test_send_throws_with_bad_credential(self): | ||
| bad_credential = "I am a bad credential" | ||
| with pytest.raises(ValueError, match="The provided credential should be an instance of AzureSasCredential or AzureKeyCredential"): | ||
| with pytest.raises(ValueError, match="The provided credential should be an instance of a TokenCredential, AzureSasCredential or AzureKeyCredential"): | ||
| client = EventGridPublisherClient("eventgrid_endpoint", bad_credential) | ||
|
|
||
| @pytest.mark.live_test_only | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why live only?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. little tricky to generate recording given we use resource group preparers and envvars for secrets |
||
| @CachedResourceGroupPreparer(name_prefix='eventgridtest') | ||
| @CachedEventGridTopicPreparer(name_prefix='eventgridtest') | ||
| def test_send_token_credential(self, resource_group, eventgrid_topic, eventgrid_topic_primary_key, eventgrid_topic_endpoint): | ||
| credential = self.generate_oauth_token() | ||
| client = EventGridPublisherClient(self.get_oauth_endpoint(), credential) | ||
| eg_event = EventGridEvent( | ||
| subject="sample", | ||
| data={"sample": "eventgridevent"}, | ||
| event_type="Sample.EventGrid.Event", | ||
| data_version="2.0" | ||
| ) | ||
| client.send(eg_event) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, this reminds me I need to add this for .NET 😄