Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using Azure.Data.AppConfiguration;
using Microsoft.Extensions.Azure;
using System;
using System.Collections.Generic;

namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Afd
{
internal class AfdConfigurationClientManager : IConfigurationClientManager
{
private readonly ConfigurationClientWrapper _clientWrapper;

public AfdConfigurationClientManager(
IAzureClientFactory<ConfigurationClient> clientFactory,
Uri endpoint)
{
if (clientFactory == null)
{
throw new ArgumentNullException(nameof(clientFactory));
}

if (endpoint == null)
{
throw new ArgumentNullException(nameof(endpoint));
}

_clientWrapper = new ConfigurationClientWrapper(endpoint, clientFactory.CreateClient(endpoint.AbsoluteUri));
}

public IEnumerable<ConfigurationClient> GetClients()
{
return new List<ConfigurationClient> { _clientWrapper.Client };
}

public void RefreshClients()
{
return;
}

public bool UpdateSyncToken(Uri endpoint, string syncToken)
{
return false;
}

public Uri GetEndpointForClient(ConfigurationClient client)
{
if (client == null)
{
throw new ArgumentNullException(nameof(client));
}

return _clientWrapper.Client == client ? _clientWrapper.Endpoint : null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using Azure.Core;
using Azure.Core.Pipeline;
using System;

namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Afd
{
/// <summary>
/// HTTP pipeline policy that removes Authorization and Sync-Token headers from outgoing requests.
/// </summary>
internal class AfdPolicy : HttpPipelinePolicy
{
private const string AuthorizationHeader = "Authorization";
private const string SyncTokenHeader = "Sync-Token";

/// <summary>
/// Processes the HTTP message and removes Authorization and Sync-Token headers.
/// </summary>
/// <param name="message">The HTTP message.</param>
/// <param name="pipeline">The pipeline.</param>
public override void Process(HttpMessage message, ReadOnlyMemory<HttpPipelinePolicy> pipeline)
{
message.Request.Headers.Remove(AuthorizationHeader);

message.Request.Headers.Remove(SyncTokenHeader);

ProcessNext(message, pipeline);
}

/// <summary>
/// Processes the HTTP message and removes Authorization and Sync-Token headers.
/// </summary>
/// <param name="message">The HTTP message.</param>
/// <param name="pipeline">The pipeline.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public override async System.Threading.Tasks.ValueTask ProcessAsync(HttpMessage message, ReadOnlyMemory<HttpPipelinePolicy> pipeline)
{
message.Request.Headers.Remove(AuthorizationHeader);

message.Request.Headers.Remove(SyncTokenHeader);

await ProcessNextAsync(message, pipeline).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using Azure.Core;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Afd
{
/// <summary>
/// A token credential that provides an empty token.
/// </summary>
internal class EmptyTokenCredential : TokenCredential
{
/// <summary>
/// Gets an empty token.
/// </summary>
/// <param name="requestContext">The context of the token request.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <returns>An empty access token.</returns>
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
return new AccessToken(string.Empty, DateTimeOffset.MaxValue);
}

/// <summary>
/// Asynchronously gets an empty token.
/// </summary>
/// <param name="requestContext">The context of the token request.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains an empty access token.</returns>
public override ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
return new ValueTask<AccessToken>(new AccessToken(string.Empty, DateTimeOffset.MaxValue));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Azure.Core;
using Azure.Data.AppConfiguration;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Afd;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.AzureKeyVault;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement;
Expand Down Expand Up @@ -132,7 +133,7 @@ internal IEnumerable<IKeyValueAdapter> Adapters
/// <summary>
/// Options used to configure the client used to communicate with Azure App Configuration.
/// </summary>
internal ConfigurationClientOptions ClientOptions { get; } = GetDefaultClientOptions();
internal ConfigurationClientOptions ClientOptions { get; private set; } = GetDefaultClientOptions();

/// <summary>
/// Flag to indicate whether Key Vault options have been configured.
Expand All @@ -154,6 +155,11 @@ internal IEnumerable<IKeyValueAdapter> Adapters
/// </summary>
internal StartupOptions Startup { get; set; } = new StartupOptions();

/// <summary>
/// Gets a value indicating whether Azure Front Door is used.
/// </summary>
internal bool IsAfdUsed { get; private set; }

/// <summary>
/// Client factory that is responsible for creating instances of ConfigurationClient.
/// </summary>
Expand Down Expand Up @@ -186,11 +192,12 @@ public AzureAppConfigurationOptions()
public AzureAppConfigurationOptions SetClientFactory(IAzureClientFactory<ConfigurationClient> factory)
{
ClientFactory = factory ?? throw new ArgumentNullException(nameof(factory));

return this;
}

/// <summary>
/// Specify what key-values to include in the configuration provider.
/// Specifies what key-values to include in the configuration provider.
/// <see cref="Select"/> can be called multiple times to include multiple sets of key-values.
/// </summary>
/// <param name="keyFilter">
Expand Down Expand Up @@ -262,7 +269,7 @@ public AzureAppConfigurationOptions Select(string keyFilter, string labelFilter
}

/// <summary>
/// Specify a snapshot and include its contained key-values in the configuration provider.
/// Specifies a snapshot and include its contained key-values in the configuration provider.
/// <see cref="SelectSnapshot"/> can be called multiple times to include key-values from multiple snapshots.
/// </summary>
/// <param name="name">The name of the snapshot in Azure App Configuration.</param>
Expand Down Expand Up @@ -362,6 +369,11 @@ public AzureAppConfigurationOptions Connect(string connectionString)
/// </param>
public AzureAppConfigurationOptions Connect(IEnumerable<string> connectionStrings)
{
if (IsAfdUsed)
{
throw new InvalidOperationException(ErrorMessages.ConnectionConflict);
}

if (connectionStrings == null || !connectionStrings.Any())
{
throw new ArgumentNullException(nameof(connectionStrings));
Expand Down Expand Up @@ -405,6 +417,11 @@ public AzureAppConfigurationOptions Connect(Uri endpoint, TokenCredential creden
/// <param name="credential">Token credential to use to connect.</param>
public AzureAppConfigurationOptions Connect(IEnumerable<Uri> endpoints, TokenCredential credential)
{
if (IsAfdUsed)
{
throw new InvalidOperationException(ErrorMessages.ConnectionConflict);
}

if (endpoints == null || !endpoints.Any())
{
throw new ArgumentNullException(nameof(endpoints));
Expand All @@ -416,12 +433,40 @@ public AzureAppConfigurationOptions Connect(IEnumerable<Uri> endpoints, TokenCre
}

Credential = credential ?? throw new ArgumentNullException(nameof(credential));

Endpoints = endpoints;
ConnectionStrings = null;
return this;
}

/// <summary>
/// Connect the provider to Azure Front Door endpoint.
/// </summary>
/// <param name="endpoint">The endpoint of the Azure Front Door instance to connect to.</param>
public AzureAppConfigurationOptions ConnectAzureFrontDoor(Uri endpoint)
{
if ((Credential != null && !(Credential is EmptyTokenCredential)) || (ConnectionStrings?.Any() ?? false))
{
throw new InvalidOperationException(ErrorMessages.ConnectionConflict);
}

if (IsAfdUsed)
{
throw new InvalidOperationException(ErrorMessages.AfdConnectionConflict);
}

if (endpoint == null)
{
throw new ArgumentNullException(nameof(endpoint));
}

Credential ??= new EmptyTokenCredential();

Endpoints = new List<Uri>() { endpoint };
ConnectionStrings = null;
IsAfdUsed = true;
return this;
}

/// <summary>
/// Trims the provided prefix from the keys of all key-values retrieved from Azure App Configuration.
/// </summary>
Expand Down
Loading