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

EG API updates #18274

Merged
merged 3 commits into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 5 additions & 5 deletions sdk/eventgrid/Azure.Messaging.EventGrid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ Publishing events to Event Grid is performed using the `EventGridPublisherClient
List<EventGridEvent> eventsList = new List<EventGridEvent>
{
new EventGridEvent(
"This is the event data",
"ExampleEventSubject",
"Example.EventType",
"1.0")
"1.0",
"This is the event data")
};

// Send the events
Expand Down Expand Up @@ -138,10 +138,10 @@ To publish events to any topic in an Event Domain, push the events to the domain
List<EventGridEvent> eventsList = new List<EventGridEvent>
{
new EventGridEvent(
"This is the event data",
"ExampleEventSubject",
"Example.EventType",
"1.0")
"1.0",
"This is the event data")
{
Topic = "MyTopic"
}
Expand Down Expand Up @@ -185,7 +185,7 @@ foreach (CloudEvent cloudEvent in cloudEvents)
TestPayload testPayload = await cloudEvent.GetDataAsync<TestPayload>(myCustomSerializer);
Console.WriteLine(testPayload.Name);
break;
case SystemEventMappings.StorageBlobDeletedEventName:
case SystemEventNames.StorageBlobDeleted:
// Example for deserializing system events using GetData<T>
StorageBlobDeletedEventData blobDeleted = cloudEvent.GetData<StorageBlobDeletedEventData>();
Console.WriteLine(blobDeleted.BlobType);
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ Note on `EventGridEvent`: each `EventGridEvent` has a set of required, non-nulla
List<EventGridEvent> eventsList = new List<EventGridEvent>
{
new EventGridEvent(
"This is the event data",
"ExampleEventSubject",
"Example.EventType",
"1.0")
"1.0",
"This is the event data")
};

// Send the events
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ To publish events to any topic in an Event Domain, push the events to the domain
List<EventGridEvent> eventsList = new List<EventGridEvent>
{
new EventGridEvent(
"This is the event data",
"ExampleEventSubject",
"Example.EventType",
"1.0")
"1.0",
"This is the event data")
{
Topic = "MyTopic"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ foreach (CloudEvent cloudEvent in cloudEvents)
TestPayload testPayload = await cloudEvent.GetDataAsync<TestPayload>(myCustomSerializer);
Console.WriteLine(testPayload.Name);
break;
case SystemEventMappings.StorageBlobDeletedEventName:
case SystemEventNames.StorageBlobDeleted:
// Example for deserializing system events using GetData<T>
StorageBlobDeletedEventData blobDeleted = cloudEvent.GetData<StorageBlobDeletedEventData>();
Console.WriteLine(blobDeleted.BlobType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@
<Compile Include="$(AzureCoreSharedSources)Argument.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)AzureResourceProviderNamespaceAttribute.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\core\Azure.Core\src\Azure.Core.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -20,12 +19,12 @@ public class EventGridEvent
{
/// <summary> Initializes a new instance of <see cref="EventGridEvent"/>. </summary>
/// <param name="subject"> A resource path relative to the topic path. </param>
/// <param name="data"> Event data specific to the event type. </param>
/// <param name="eventType"> The type of the event that occurred. For example, "Contoso.Items.ItemReceived". </param>
/// <param name="dataVersion"> The schema version of the data object. </param>
/// <param name="data"> Event data specific to the event type. </param>
/// <param name="dataSerializationType">The type to use when serializing the data.
/// If not specified, <see cref="object.GetType()"/> will be used on <paramref name="data"/>.</param>
public EventGridEvent(object data, string subject, string eventType, string dataVersion, Type dataSerializationType = default)
public EventGridEvent(string subject, string eventType, string dataVersion, object data, Type dataSerializationType = default)
{
Argument.AssertNotNull(subject, nameof(subject));
Argument.AssertNotNull(data, nameof(data));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,7 @@ public EventGridPublisherClient(Uri endpoint, AzureKeyCredential credential)
{
}

/// <summary>Initalizes a new instance of the <see cref="EventGridPublisherClient"/> class.</summary>
/// <param name="endpoint">The topic endpoint. For example, "https://TOPIC-NAME.REGION-NAME-1.eventgrid.azure.net/api/events".</param>
/// <param name="credential">The Shared Access Signature credential used to authenticate with the service.This signature
/// can be constructed using <see cref="BuildSharedAccessSignature"/>.</param>
public EventGridPublisherClient(Uri endpoint, AzureSasCredential credential)
: this(endpoint, credential, new EventGridPublisherClientOptions())
{
}

/// <summary>Initalizes a new instance of the <see cref="EventGridPublisherClient"/> class.</summary>
/// <summary>Initalizes a new instance of the <see cref="EventGridPublisherClient"/> class.</summary>
/// <param name="endpoint">The topic endpoint. For example, "https://TOPIC-NAME.REGION-NAME-1.eventgrid.azure.net/api/events".</param>
/// <param name="credential">The key credential used to authenticate with the service.</param>
/// <param name="options">The set of options to use for configuring the client.</param>
Expand Down Expand Up @@ -137,9 +128,9 @@ private async Task<Response> SendCloudNativeCloudEventsInternalAsync(ReadOnlyMem
/// </summary>
/// <param name="endpoint">The topic endpoint. For example, "https://TOPIC-NAME.REGION-NAME-1.eventgrid.azure.net/api/events".</param>
/// <param name="credential">The Shared Access Signature credential used to connect to Azure. This signature
/// can be constructed using <see cref="BuildSharedAccessSignature"/>.</param>
/// can be constructed using the <see cref="EventGridSasBuilder"/>.</param>
/// <param name="options">The set of options to use for configuring the client.</param>
public EventGridPublisherClient(Uri endpoint, AzureSasCredential credential, EventGridPublisherClientOptions options)
public EventGridPublisherClient(Uri endpoint, AzureSasCredential credential, EventGridPublisherClientOptions options = default)
{
Argument.AssertNotNull(credential, nameof(credential));
options ??= new EventGridPublisherClientOptions();
Expand Down Expand Up @@ -372,38 +363,6 @@ private async Task<Response> PublishCustomEventsInternal(IEnumerable<object> eve
}
}

/// <summary>
/// Creates a SAS token for use with the Event Grid service.
/// </summary>
/// <param name="endpoint">The path for the event grid topic to which you're sending events. For example, "https://TOPIC-NAME.REGION-NAME.eventgrid.azure.net/eventGrid/api/events".</param>
/// <param name="expirationUtc">Time at which the SAS token becomes invalid for authentication.</param>
/// <param name="key">The key credential used to generate the token.</param>
/// <param name="apiVersion">The service version to use when handling requests made with the SAS token.</param>
/// <returns>The generated SAS token string.</returns>
public static string BuildSharedAccessSignature(Uri endpoint, DateTimeOffset expirationUtc, AzureKeyCredential key, EventGridPublisherClientOptions.ServiceVersion apiVersion = EventGridPublisherClientOptions.LatestVersion)
{
const char Resource = 'r';
const char Expiration = 'e';
const char Signature = 's';

var uriBuilder = new RequestUriBuilder();
uriBuilder.Reset(endpoint);
uriBuilder.AppendQuery("api-version", apiVersion.GetVersionString(), true);
string encodedResource = HttpUtility.UrlEncode(uriBuilder.ToString());
var culture = CultureInfo.CreateSpecificCulture("en-US");
var encodedExpirationUtc = HttpUtility.UrlEncode(expirationUtc.ToString(culture));

string unsignedSas = $"{Resource}={encodedResource}&{Expiration}={encodedExpirationUtc}";
using (var hmac = new HMACSHA256(Convert.FromBase64String(key.Key)))
{
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(unsignedSas)));
string encodedSignature = HttpUtility.UrlEncode(signature);
string signedSas = $"{unsignedSas}&{Signature}={encodedSignature}";

return signedSas;
}
}

private JsonDocument SerializeObjectToJsonDocument(object data, Type type, CancellationToken cancellationToken)
{
using (MemoryStream stream = new MemoryStream())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using Azure.Core;

namespace Azure.Messaging.EventGrid
{
/// <summary>
/// This <see cref="EventGridSasBuilder"/> is used to generate a Shared Access Signature (SAS) for an Azure Event Grid topic.
/// </summary>
public class EventGridSasBuilder
{
/// <summary>
/// Initializes a new instance of the <see cref="EventGridSasBuilder"/> class.
/// </summary>
/// <param name="endpoint">The endpoint to generate a shared access signature for.
/// For example, "https://TOPIC-NAME.REGION-NAME.eventgrid.azure.net/eventGrid/api/events".</param>
/// <param name="expiresOn">The time at which the shared access signature should expire.</param>
public EventGridSasBuilder(Uri endpoint, DateTimeOffset expiresOn)
{
Argument.AssertNotNull(endpoint, nameof(endpoint));
Endpoint = endpoint;
ExpiresOn = expiresOn;
}

/// <summary>
/// Gets or sets the endpoint to generate a shared access signature for.
/// </summary>
public Uri Endpoint
{
get
{
return _endpoint;
}
set
{
Argument.AssertNotNull(value, nameof(value));
_endpoint = value;
}
}

private Uri _endpoint;

/// <summary>
/// Gets or sets the time at which the shared access signature should expire.
/// </summary>
public DateTimeOffset ExpiresOn { get; set; }

/// <summary>
/// Gets or sets the service version to use when generating the shared access signature.
/// </summary>
public EventGridPublisherClientOptions.ServiceVersion ApiVersion { get; set; } = EventGridPublisherClientOptions.LatestVersion;

/// <summary>
/// Generates a shared access signature that can be used to authenticate with a topic.
/// The signature can be used as the input to the <see cref="AzureSasCredential(string)"/> constructor.
/// This credential can then be passed to the <see cref="EventGridPublisherClient(Uri, AzureSasCredential, EventGridPublisherClientOptions)"/> constructor.
/// </summary>
/// <param name="key">The <see cref="AzureKeyCredential"/> to use to authenticate with the service
/// when generating the shared access signature.</param>
/// <returns>A shared access signature that can be used to authenticate with an Event Grid topic.</returns>
public string GenerateSas(AzureKeyCredential key)
{
Argument.AssertNotNull(key, nameof(key));
const char Resource = 'r';
const char Expiration = 'e';
const char Signature = 's';

var uriBuilder = new RequestUriBuilder();
uriBuilder.Reset(Endpoint);
uriBuilder.AppendQuery("api-version", ApiVersion.GetVersionString(), true);
string encodedResource = HttpUtility.UrlEncode(uriBuilder.ToString());
var encodedExpirationUtc = HttpUtility.UrlEncode(ExpiresOn.ToString(CultureInfo.InvariantCulture));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was previously using "en-us" for the culture as that is what was referenced in https://docs.microsoft.com/en-us/azure/event-grid/security-authenticate-publishing-clients#generate-sas-token-programmatically

But InvariantCulture seems to work fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slight preference for keeping en-US since that's what was documented by the service, but I agree in practice there is unlikely to be a difference between the two cultures in practice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, will revert.


string unsignedSas = $"{Resource}={encodedResource}&{Expiration}={encodedExpirationUtc}";
using (var hmac = new HMACSHA256(Convert.FromBase64String(key.Key)))
{
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(unsignedSas)));
string encodedSignature = HttpUtility.UrlEncode(signature);
string signedSas = $"{unsignedSas}&{Signature}={encodedSignature}";

return signedSas;
}
}
}
}
Loading