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

[Maps] Support SAS authentication #37310

Merged
merged 22 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
/sdk/entra/ @pallavit @jsquire
/sdk/graphrbac/ @pallavit @jsquire
/sdk/machinelearningservices/ @pallavit @jsquire
/sdk/maps/ @pallavit @jsquire
/sdk/maps/ @pallavit @jsquire @dubiety @khmic5 @andykao1213
# Note there are some sub-rules for this directory, defined below
/sdk/operationalinsights/ @pallavit @jsquire
/sdk/purview/ @pallavit @jsquire
Expand Down
6 changes: 1 addition & 5 deletions sdk/maps/Azure.Maps.Common/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@

### Features Added

### Breaking Changes

### Bugs Fixed

### Other Changes
- Create SAS credential policy

## 1.0.0-beta.3 (2022-11-08)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@ namespace Azure.Maps
public static bool operator !=(Azure.Maps.LocalizedMapView left, Azure.Maps.LocalizedMapView right) { throw null; }
public override string ToString() { throw null; }
}
public partial class MapsSasCredentialPolicy : Azure.Core.Pipeline.HttpPipelineSynchronousPolicy
{
public MapsSasCredentialPolicy(Azure.AzureSasCredential credential) { }
public override void OnSendingRequest(Azure.Core.HttpMessage message) { }
}
}
33 changes: 33 additions & 0 deletions sdk/maps/Azure.Maps.Common/src/MapsSasCredentialPolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Core;
using Azure.Core.Pipeline;

namespace Azure.Maps
{
/// <summary> The MapsSasCredentialPolicy used for SAS authentication. </summary>
public class MapsSasCredentialPolicy : HttpPipelineSynchronousPolicy
{
private readonly string _sasAuthenticationHeader = "Authorization";
private readonly string _sasPrefix = "jwt-sas";
private readonly AzureSasCredential _credential;

/// <summary>
/// Initializes a new instance of the <see cref="MapsSasCredentialPolicy"/> class.
/// </summary>
/// <param name="credential">The <see cref="AzureSasCredential"/> used to authenticate requests.</param>
public MapsSasCredentialPolicy(AzureSasCredential credential)
{
Argument.AssertNotNull(credential, nameof(credential));
_credential = credential;
}

/// <inheritdoc/>
public override void OnSendingRequest(HttpMessage message)
{
base.OnSendingRequest(message);
message.Request.Headers.SetValue(_sasAuthenticationHeader, _sasPrefix + " " + _credential.Signature);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Azure.Maps;
using Azure.Core.TestFramework;
using NUnit.Framework;

namespace Azure.Maps.Tests
{
public class MapsSasCredentialPolicyTests
{
[Test]
public void MapsSasCredentialPolicyTest()
{
// Exception case
Assert.Throws<ArgumentNullException>(() => new MapsSasCredentialPolicy(null));

// Normal case
var credentialPolicy = new MapsSasCredentialPolicy(new AzureSasCredential("Sas Token"));

Assert.NotNull(credentialPolicy);
}
}
}
6 changes: 1 addition & 5 deletions sdk/maps/Azure.Maps.Search/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@

### Features Added

### Breaking Changes

### Bugs Fixed

### Other Changes
- Support SAS authentication

## 1.0.0-beta.3 (2022-11-08)

Expand Down
60 changes: 60 additions & 0 deletions sdk/maps/Azure.Maps.Search/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,66 @@ string clientId = "<My Map Account Client Id>";
MapsSearchClient client = new MapsSearchClient(credential, clientId);
```

#### Shared Access Signature (SAS) Authentication

Shared access signature (SAS) tokens are authentication tokens created using the JSON Web token (JWT) format and are cryptographically signed to prove authentication for an application to the Azure Maps REST API.

Before integrating SAS token authentication, we need to install `Azure.ResourceManager` and `Azure.ResourceManager.Maps` (version `1.1.0-beta.2` or higher):

```powershell
dotnet add package Azure.ResourceManager
dotnet add package Azure.ResourceManager.Maps --prerelease
```

In the code, we need to import the following lines for both Azure Maps SDK and ResourceManager:

```C# Snippet:SearchImportNamespaces
using Azure.Core.GeoJson;
using Azure.Maps.Search;
using Azure.Maps.Search.Models;
```

```C# Snippet:SasAuthImportNamespaces
using Azure.Core;
using Azure.ResourceManager;
using Azure.ResourceManager.Maps;
using Azure.ResourceManager.Maps.Models;
```

And then we can get SAS token via [List Sas](https://learn.microsoft.com/rest/api/maps-management/accounts/list-sas?tabs=HTTP) API and assign it to `MapsSearchClient`. In the follow code sample, we fetch a specific maps account resouce, and create a SAS token for 1 day expiry time when the code is executed.

```C# Snippet:InstantiateSearchClientViaSas
// Get your azure access token, for more details of how Azure SDK get your access token, please refer to https://learn.microsoft.com/en-us/dotnet/azure/sdk/authentication?tabs=command-line
TokenCredential cred = new DefaultAzureCredential();
// Authenticate your client
ArmClient armClient = new ArmClient(cred);

string subscriptionId = "MyMapsSubscriptionId";
string resourceGroupName = "MyMapsResourceGroupName";
string accountName = "MyMapsAccountName";

// Get maps account resource
ResourceIdentifier mapsAccountResourceId = MapsAccountResource.CreateResourceIdentifier(subscriptionId, resourceGroupName, accountName);
MapsAccountResource mapsAccount = armClient.GetMapsAccountResource(mapsAccountResourceId);

// Assign SAS token information
// Every time you want to SAS token, update the principal ID, max rate, start and expiry time
string principalId = "MyManagedIdentityObjectId";
int maxRatePerSecond = 500;

// Set start and expiry time for the SAS token in round-trip date/time format
DateTime now = DateTime.Now;
string start = now.ToString("O");
string expiry = now.AddDays(1).ToString("O");

MapsAccountSasContent sasContent = new MapsAccountSasContent(MapsSigningKey.PrimaryKey, principalId, maxRatePerSecond, start, expiry);
Response<MapsAccountSasToken> sas = mapsAccount.GetSas(sasContent);

// Create a SearchClient that will authenticate via SAS token
AzureSasCredential sasCredential = new AzureSasCredential(sas.Value.AccountSasToken);
MapsSearchClient client = new MapsSearchClient(sasCredential);
```

## Key concepts

`MapsSearchClient` is designed to:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ public partial class MapsSearchClient
protected MapsSearchClient() { }
public MapsSearchClient(Azure.AzureKeyCredential credential) { }
public MapsSearchClient(Azure.AzureKeyCredential credential, Azure.Maps.Search.MapsSearchClientOptions options) { }
public MapsSearchClient(Azure.AzureSasCredential credential) { }
public MapsSearchClient(Azure.AzureSasCredential credential, Azure.Maps.Search.MapsSearchClientOptions options) { }
public MapsSearchClient(Azure.Core.TokenCredential credential, string clientId) { }
public MapsSearchClient(Azure.Core.TokenCredential credential, string clientId, Azure.Maps.Search.MapsSearchClientOptions options) { }
public virtual Azure.Maps.Search.FuzzySearchBatchOperation FuzzyBatchSearch(Azure.WaitUntil waitUntil, System.Collections.Generic.IEnumerable<Azure.Maps.Search.Models.FuzzySearchQuery> queries, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
Expand Down
2 changes: 1 addition & 1 deletion sdk/maps/Azure.Maps.Search/samples/FuzzySearchSamples.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ To use these samples, you'll first need to set up resources. See [getting starte

## Import the namespaces

```C# Snippet:SearchImportNamespace
```C# Snippet:SearchImportNamespaces
using Azure.Core.GeoJson;
using Azure.Maps.Search;
using Azure.Maps.Search.Models;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ To use these samples, you'll first need to set up resources. See [getting starte

## Import the namespaces

```C# Snippet:SearchImportNamespace
```C# Snippet:SearchImportNamespaces
using Azure.Core.GeoJson;
using Azure.Maps.Search;
using Azure.Maps.Search.Models;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ To use these samples, you'll first need to set up resources. See [getting starte

## Import the namespaces

```C# Snippet:SearchImportNamespace
```C# Snippet:SearchImportNamespaces
using Azure.Core.GeoJson;
using Azure.Maps.Search;
using Azure.Maps.Search.Models;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ To use these samples, you'll first need to set up resources. See [getting starte

## Import the namespaces

```C# Snippet:SearchImportNamespace
```C# Snippet:SearchImportNamespaces
using Azure.Core.GeoJson;
using Azure.Maps.Search;
using Azure.Maps.Search.Models;
Expand Down
32 changes: 31 additions & 1 deletion sdk/maps/Azure.Maps.Search/src/MapsSearchClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Core.GeoJson;
using Azure.Core.Pipeline;
using Azure.Maps;
using Azure.Maps.Search.Models;
using Azure.Core.GeoJson;

namespace Azure.Maps.Search
{
Expand Down Expand Up @@ -93,6 +94,35 @@ public MapsSearchClient(TokenCredential credential, string clientId, MapsSearchC
RestClient = new SearchRestClient(_clientDiagnostics, _pipeline, endpoint, clientId, options.Version);
}

/// <summary> Initializes a new instance of MapsSearchClient. </summary>
/// <param name="credential"> The Shared Access Signature credential used to connect to Azure. This signature
/// can be constructed using the <see cref="AzureSasCredential"/>.</param>
public MapsSearchClient(AzureSasCredential credential)
{
Argument.AssertNotNull(credential, nameof(credential));

var endpoint = new Uri("https://atlas.microsoft.com");
var options = new MapsSearchClientOptions();
_clientDiagnostics = new ClientDiagnostics(options);
_pipeline = HttpPipelineBuilder.Build(options, new MapsSasCredentialPolicy(credential));
RestClient = new SearchRestClient(_clientDiagnostics, _pipeline, endpoint, null, options.Version);
}

/// <summary> Initializes a new instance of MapsSearchClient. </summary>
/// <param name="credential"> The Shared Access Signature credential used to connect to Azure. This signature
/// can be constructed using the <see cref="AzureSasCredential"/>.</param>
/// <param name="options"> The options for configuring the client. </param>
public MapsSearchClient(AzureSasCredential credential, MapsSearchClientOptions options)
{
Argument.AssertNotNull(credential, nameof(credential));

var endpoint = options.Endpoint;
options ??= new MapsSearchClientOptions();
_clientDiagnostics = new ClientDiagnostics(options);
_pipeline = HttpPipelineBuilder.Build(options, new MapsSasCredentialPolicy(credential));
RestClient = new SearchRestClient(_clientDiagnostics, _pipeline, endpoint, null, options.Version);
}

/// <summary>
/// The Get Polygon service allows you to request the geometry data such as a city or country outline for a set of entities, previously retrieved from an Online Search request in GeoJSON format. The geometry ID is returned in the sourceGeometry object under &quot;geometry&quot; and &quot;id&quot; in either a Search Address or Search Fuzzy call.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
<PackageReference Include="NUnit3TestAdapter" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
<PackageReference Include="Azure.ResourceManager" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="$(AzureCoreTestFramework)" />
<ProjectReference Include="..\src\Azure.Maps.Search.csproj" />
<ProjectReference Include="..\..\Azure.ResourceManager.Maps\src\Azure.ResourceManager.Maps.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
#region Snippet:SearchImportNamespace
#region Snippet:SearchImportNamespaces
using Azure.Core.GeoJson;
using Azure.Maps.Search;
using Azure.Maps.Search.Models;
#endregion
using Azure.Core.TestFramework;
#region Snippet:SasAuthImportNamespaces
using Azure.Core;
using Azure.ResourceManager;
using Azure.ResourceManager.Maps;
using Azure.ResourceManager.Maps.Models;
#endregion

using NUnit.Framework;

Expand All @@ -37,6 +43,41 @@ public void SearchClientViaAAD()
#endregion
}

public void SearchClientViaSas()
{
#region Snippet:InstantiateSearchClientViaSas
// Get your azure access token, for more details of how Azure SDK get your access token, please refer to https://learn.microsoft.com/en-us/dotnet/azure/sdk/authentication?tabs=command-line
TokenCredential cred = new DefaultAzureCredential();
// Authenticate your client
ArmClient armClient = new ArmClient(cred);

string subscriptionId = "MyMapsSubscriptionId";
string resourceGroupName = "MyMapsResourceGroupName";
string accountName = "MyMapsAccountName";

// Get maps account resource
ResourceIdentifier mapsAccountResourceId = MapsAccountResource.CreateResourceIdentifier(subscriptionId, resourceGroupName, accountName);
MapsAccountResource mapsAccount = armClient.GetMapsAccountResource(mapsAccountResourceId);

// Assign SAS token information
// Every time you want to SAS token, update the principal ID, max rate, start and expiry time
string principalId = "MyManagedIdentityObjectId";
int maxRatePerSecond = 500;

// Set start and expiry time for the SAS token in round-trip date/time format
DateTime now = DateTime.Now;
string start = now.ToString("O");
string expiry = now.AddDays(1).ToString("O");

MapsAccountSasContent sasContent = new MapsAccountSasContent(MapsSigningKey.PrimaryKey, principalId, maxRatePerSecond, start, expiry);
Response<MapsAccountSasToken> sas = mapsAccount.GetSas(sasContent);

// Create a SearchClient that will authenticate via SAS token
AzureSasCredential sasCredential = new AzureSasCredential(sas.Value.AccountSasToken);
MapsSearchClient client = new MapsSearchClient(sasCredential);
#endregion
}

[Test]
public async Task GetPolygons()
{
Expand Down