aspnetcore/src/Microsoft.AspNetCore.DataPr.../AzureDataProtectionBuilderE...

176 lines
7.6 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.DataProtection.AzureStorage;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
namespace Microsoft.AspNetCore.DataProtection
{
/// <summary>
/// Contains Azure-specific extension methods for modifying a
/// <see cref="IDataProtectionBuilder"/>.
/// </summary>
public static class AzureDataProtectionBuilderExtensions
{
/// <summary>
/// Configures the data protection system to persist keys to the specified path
/// in Azure Blob Storage.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="storageAccount">The <see cref="CloudStorageAccount"/> which
/// should be utilized.</param>
/// <param name="relativePath">A relative path where the key file should be
/// stored, generally specified as "/containerName/[subDir/]keys.xml".</param>
/// <returns>The value <paramref name="builder"/>.</returns>
/// <remarks>
/// The container referenced by <paramref name="relativePath"/> must already exist.
/// </remarks>
public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, CloudStorageAccount storageAccount, string relativePath)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (storageAccount == null)
{
throw new ArgumentNullException(nameof(storageAccount));
}
if (relativePath == null)
{
throw new ArgumentNullException(nameof(relativePath));
}
// Simply concatenate the root storage endpoint with the relative path,
// which includes the container name and blob name.
var uriBuilder = new UriBuilder(storageAccount.BlobEndpoint);
uriBuilder.Path = uriBuilder.Path.TrimEnd('/') + "/" + relativePath.TrimStart('/');
// We can create a CloudBlockBlob from the storage URI and the creds.
var blobAbsoluteUri = uriBuilder.Uri;
var credentials = storageAccount.Credentials;
return PersistKeystoAzureBlobStorageInternal(builder, () => new CloudBlockBlob(blobAbsoluteUri, credentials));
}
/// <summary>
/// Configures the data protection system to persist keys to the specified path
/// in Azure Blob Storage.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="blobUri">The full URI where the key file should be stored.
/// The URI must contain the SAS token as a query string parameter.</param>
/// <returns>The value <paramref name="builder"/>.</returns>
/// <remarks>
/// The container referenced by <paramref name="blobUri"/> must already exist.
/// </remarks>
public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, Uri blobUri)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (blobUri == null)
{
throw new ArgumentNullException(nameof(blobUri));
}
var uriBuilder = new UriBuilder(blobUri);
// The SAS token is present in the query string.
if (string.IsNullOrEmpty(uriBuilder.Query))
{
throw new ArgumentException(
message: "URI does not have a SAS token in the query string.",
paramName: nameof(blobUri));
}
var credentials = new StorageCredentials(uriBuilder.Query);
uriBuilder.Query = null; // no longer needed
var blobAbsoluteUri = uriBuilder.Uri;
return PersistKeystoAzureBlobStorageInternal(builder, () => new CloudBlockBlob(blobAbsoluteUri, credentials));
}
/// <summary>
/// Configures the data protection system to persist keys to the specified path
/// in Azure Blob Storage.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="blobReference">The <see cref="CloudBlockBlob"/> where the
/// key file should be stored.</param>
/// <returns>The value <paramref name="builder"/>.</returns>
/// <remarks>
/// The container referenced by <paramref name="blobReference"/> must already exist.
/// </remarks>
public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, CloudBlockBlob blobReference)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (blobReference == null)
{
throw new ArgumentNullException(nameof(blobReference));
}
// We're basically just going to make a copy of this blob.
// Use (container, blobName) instead of (storageuri, creds) since the container
// is tied to an existing service client, which contains user-settable defaults
// like retry policy and secondary connection URIs.
var container = blobReference.Container;
var blobName = blobReference.Name;
return PersistKeystoAzureBlobStorageInternal(builder, () => container.GetBlockBlobReference(blobName));
}
/// <summary>
/// Configures the data protection system to persist keys to the specified path
/// in Azure Blob Storage.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="container">The <see cref="CloudBlobContainer"/> in which the
/// key file should be stored.</param>
/// <param name="blobName">The name of the key file, generally specified
/// as "[subdir/]keys.xml"</param>
/// <returns>The value <paramref name="builder"/>.</returns>
/// <remarks>
/// The container referenced by <paramref name="container"/> must already exist.
/// </remarks>
public static IDataProtectionBuilder PersistKeysToAzureBlobStorage(this IDataProtectionBuilder builder, CloudBlobContainer container, string blobName)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
if (blobName == null)
{
throw new ArgumentNullException(nameof(blobName));
}
return PersistKeystoAzureBlobStorageInternal(builder, () => container.GetBlockBlobReference(blobName));
}
// important: the Func passed into this method must return a new instance with each call
private static IDataProtectionBuilder PersistKeystoAzureBlobStorageInternal(IDataProtectionBuilder builder, Func<CloudBlockBlob> blobRefFactory)
{
builder.Services.Configure<KeyManagementOptions>(options =>
{
options.XmlRepository = new AzureBlobXmlRepository(blobRefFactory);
});
return builder;
}
}
}