Merge pull request #19655 from dotnet-maestro-bot/merge/release/5.0-preview2-to-master

[automated] Merge branch 'release/5.0-preview2' => 'master'
This commit is contained in:
Doug Bunting 2020-03-09 15:00:23 -07:00 committed by GitHub
commit c4305ff51e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 13 additions and 1306 deletions

View File

@ -162,10 +162,6 @@ and are generated based on the last package release.
<LatestPackageReference Include="IdentityServer4.Storage" Version="$(IdentityServer4StoragePackageVersion)" />
<LatestPackageReference Include="Libuv" Version="$(LibuvPackageVersion)" />
<LatestPackageReference Include="MessagePack" Version="$(MessagePackPackageVersion)" />
<LatestPackageReference Include="Microsoft.Azure.KeyVault" Version="$(MicrosoftAzureKeyVaultPackageVersion)" />
<LatestPackageReference Include="Microsoft.Azure.Storage.Blob" Version="$(MicrosoftAzureStorageBlobPackageVersion)" />
<LatestPackageReference Include="Microsoft.Data.OData" Version="$(MicrosoftDataODataPackageVersion)" />
<LatestPackageReference Include="Microsoft.Data.Services.Client" Version="$(MicrosoftDataServicesClientPackageVersion)" />
<LatestPackageReference Include="Mono.Cecil" Version="$(MonoCecilPackageVersion)" />
<LatestPackageReference Include="Moq" Version="$(MoqPackageVersion)" />
<LatestPackageReference Include="Newtonsoft.Json.Bson" Version="$(NewtonsoftJsonBsonPackageVersion)" />

View File

@ -6,8 +6,6 @@
<Project>
<ItemGroup>
<ProjectReferenceProvider Include="Microsoft.AspNetCore.JsonPatch" ProjectPath="$(RepoRoot)src\Features\JsonPatch\src\Microsoft.AspNetCore.JsonPatch.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.AzureKeyVault" ProjectPath="$(RepoRoot)src\DataProtection\AzureKeyVault\src\Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.AzureStorage" ProjectPath="$(RepoRoot)src\DataProtection\AzureStorage\src\Microsoft.AspNetCore.DataProtection.AzureStorage.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" ProjectPath="$(RepoRoot)src\DataProtection\EntityFrameworkCore\src\Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" ProjectPath="$(RepoRoot)src\DataProtection\StackExchangeRedis\src\Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.TestHost" ProjectPath="$(RepoRoot)src\Hosting\TestHost\src\Microsoft.AspNetCore.TestHost.csproj" />

View File

@ -189,8 +189,6 @@
<MicrosoftAspNetWebApiClientPackageVersion>5.2.6</MicrosoftAspNetWebApiClientPackageVersion>
<MonoWebAssemblyInteropPackageVersion>3.1.1-preview4.19614.4</MonoWebAssemblyInteropPackageVersion>
<!-- Partner teams -->
<MicrosoftAzureKeyVaultPackageVersion>2.3.2</MicrosoftAzureKeyVaultPackageVersion>
<MicrosoftAzureStorageBlobPackageVersion>10.0.1</MicrosoftAzureStorageBlobPackageVersion>
<MicrosoftBclAsyncInterfacesPackageVersion>1.0.0</MicrosoftBclAsyncInterfacesPackageVersion>
<MicrosoftBuildPackageVersion>15.8.166</MicrosoftBuildPackageVersion>
<MicrosoftAzureSignalRPackageVersion>1.2.0</MicrosoftAzureSignalRPackageVersion>
@ -200,8 +198,6 @@
<MicrosoftCodeAnalysisCommonPackageVersion>3.0.0</MicrosoftCodeAnalysisCommonPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>3.0.0</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>3.0.0</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<MicrosoftDataODataPackageVersion>5.8.4</MicrosoftDataODataPackageVersion>
<MicrosoftDataServicesClientPackageVersion>5.8.4</MicrosoftDataServicesClientPackageVersion>
<MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>3.19.8</MicrosoftIdentityModelClientsActiveDirectoryPackageVersion>
<MicrosoftIdentityModelLoggingPackageVersion>5.5.0</MicrosoftIdentityModelLoggingPackageVersion>
<MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>5.5.0</MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion>

View File

@ -25,6 +25,7 @@ param(
[Parameter(Mandatory=$true)]
[string]$Project,
[string]$HelixQueues = "Windows.10.Amd64.Open",
[string]$TargetArchitecture = "",
[bool]$RunQuarantinedTests = $false
)
$ErrorActionPreference = 'Stop'
@ -38,4 +39,4 @@ $env:BUILD_REPOSITORY_NAME="aspnetcore"
$env:SYSTEM_TEAMPROJECT="aspnetcore"
$HelixQueues = $HelixQueues -replace ";", "%3B"
dotnet msbuild $Project /t:Helix /p:IsRequiredCheck=true /p:IsHelixDaily=true /p:HelixTargetQueues=$HelixQueues /p:RunQuarantinedTests=$RunQuarantinedTests /p:_UseHelixOpenQueues=true
dotnet msbuild $Project /t:Helix /p:TargetArchitecture="$TargetArchitecture" /p:IsRequiredCheck=true /p:IsHelixDaily=true /p:HelixTargetQueues=$HelixQueues /p:RunQuarantinedTests=$RunQuarantinedTests /p:_UseHelixOpenQueues=true

View File

@ -1,118 +0,0 @@
// 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 System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection.AzureKeyVault;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Azure.KeyVault;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace Microsoft.AspNetCore.DataProtection
{
/// <summary>
/// Contains Azure KeyVault-specific extension methods for modifying a <see cref="IDataProtectionBuilder"/>.
/// </summary>
public static class AzureDataProtectionBuilderExtensions
{
/// <summary>
/// Configures the data protection system to protect keys with specified key in Azure KeyVault.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="keyIdentifier">The Azure KeyVault key identifier used for key encryption.</param>
/// <param name="clientId">The application client id.</param>
/// <param name="certificate"></param>
/// <returns>The value <paramref name="builder"/>.</returns>
public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProtectionBuilder builder, string keyIdentifier, string clientId, X509Certificate2 certificate)
{
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentException(nameof(clientId));
}
if (certificate == null)
{
throw new ArgumentNullException(nameof(certificate));
}
KeyVaultClient.AuthenticationCallback callback =
(authority, resource, scope) => GetTokenFromClientCertificate(authority, resource, clientId, certificate);
return ProtectKeysWithAzureKeyVault(builder, new KeyVaultClient(callback), keyIdentifier);
}
private static async Task<string> GetTokenFromClientCertificate(string authority, string resource, string clientId, X509Certificate2 certificate)
{
var authContext = new AuthenticationContext(authority);
var result = await authContext.AcquireTokenAsync(resource, new ClientAssertionCertificate(clientId, certificate));
return result.AccessToken;
}
/// <summary>
/// Configures the data protection system to protect keys with specified key in Azure KeyVault.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="keyIdentifier">The Azure KeyVault key identifier used for key encryption.</param>
/// <param name="clientId">The application client id.</param>
/// <param name="clientSecret">The client secret to use for authentication.</param>
/// <returns>The value <paramref name="builder"/>.</returns>
public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProtectionBuilder builder, string keyIdentifier, string clientId, string clientSecret)
{
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentNullException(nameof(clientId));
}
if (string.IsNullOrEmpty(clientSecret))
{
throw new ArgumentNullException(nameof(clientSecret));
}
KeyVaultClient.AuthenticationCallback callback =
(authority, resource, scope) => GetTokenFromClientSecret(authority, resource, clientId, clientSecret);
return ProtectKeysWithAzureKeyVault(builder, new KeyVaultClient(callback), keyIdentifier);
}
private static async Task<string> GetTokenFromClientSecret(string authority, string resource, string clientId, string clientSecret)
{
var authContext = new AuthenticationContext(authority);
var clientCred = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenAsync(resource, clientCred);
return result.AccessToken;
}
/// <summary>
/// Configures the data protection system to protect keys with specified key in Azure KeyVault.
/// </summary>
/// <param name="builder">The builder instance to modify.</param>
/// <param name="client">The <see cref="KeyVaultClient"/> to use for KeyVault access.</param>
/// <param name="keyIdentifier">The Azure KeyVault key identifier used for key encryption.</param>
/// <returns>The value <paramref name="builder"/>.</returns>
public static IDataProtectionBuilder ProtectKeysWithAzureKeyVault(this IDataProtectionBuilder builder, KeyVaultClient client, string keyIdentifier)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (client == null)
{
throw new ArgumentNullException(nameof(client));
}
if (string.IsNullOrEmpty(keyIdentifier))
{
throw new ArgumentException(nameof(keyIdentifier));
}
var vaultClientWrapper = new KeyVaultClientWrapper(client);
builder.Services.AddSingleton<IKeyVaultWrappingClient>(vaultClientWrapper);
builder.Services.Configure<KeyManagementOptions>(options =>
{
options.XmlEncryptor = new AzureKeyVaultXmlEncryptor(vaultClientWrapper, keyIdentifier);
});
return builder;
}
}
}

View File

@ -1,52 +0,0 @@
// 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 System.IO;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.DataProtection.AzureKeyVault
{
internal class AzureKeyVaultXmlDecryptor: IXmlDecryptor
{
private readonly IKeyVaultWrappingClient _client;
public AzureKeyVaultXmlDecryptor(IServiceProvider serviceProvider)
{
_client = serviceProvider.GetService<IKeyVaultWrappingClient>();
}
public XElement Decrypt(XElement encryptedElement)
{
return DecryptAsync(encryptedElement).GetAwaiter().GetResult();
}
private async Task<XElement> DecryptAsync(XElement encryptedElement)
{
var kid = (string)encryptedElement.Element("kid");
var symmetricKey = Convert.FromBase64String((string)encryptedElement.Element("key"));
var symmetricIV = Convert.FromBase64String((string)encryptedElement.Element("iv"));
var encryptedValue = Convert.FromBase64String((string)encryptedElement.Element("value"));
var result = await _client.UnwrapKeyAsync(kid, AzureKeyVaultXmlEncryptor.DefaultKeyEncryption, symmetricKey);
byte[] decryptedValue;
using (var symmetricAlgorithm = AzureKeyVaultXmlEncryptor.DefaultSymmetricAlgorithmFactory())
{
using (var decryptor = symmetricAlgorithm.CreateDecryptor(result.Result, symmetricIV))
{
decryptedValue = decryptor.TransformFinalBlock(encryptedValue, 0, encryptedValue.Length);
}
}
using (var memoryStream = new MemoryStream(decryptedValue))
{
return XElement.Load(memoryStream);
}
}
}
}

View File

@ -1,77 +0,0 @@
// 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 System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.XmlEncryption;
using Microsoft.Azure.KeyVault.WebKey;
namespace Microsoft.AspNetCore.DataProtection.AzureKeyVault
{
internal class AzureKeyVaultXmlEncryptor : IXmlEncryptor
{
internal static string DefaultKeyEncryption = JsonWebKeyEncryptionAlgorithm.RSAOAEP;
internal static Func<SymmetricAlgorithm> DefaultSymmetricAlgorithmFactory = Aes.Create;
private readonly RandomNumberGenerator _randomNumberGenerator;
private readonly IKeyVaultWrappingClient _client;
private readonly string _keyId;
public AzureKeyVaultXmlEncryptor(IKeyVaultWrappingClient client, string keyId)
: this(client, keyId, RandomNumberGenerator.Create())
{
}
internal AzureKeyVaultXmlEncryptor(IKeyVaultWrappingClient client, string keyId, RandomNumberGenerator randomNumberGenerator)
{
_client = client;
_keyId = keyId;
_randomNumberGenerator = randomNumberGenerator;
}
public EncryptedXmlInfo Encrypt(XElement plaintextElement)
{
return EncryptAsync(plaintextElement).GetAwaiter().GetResult();
}
private async Task<EncryptedXmlInfo> EncryptAsync(XElement plaintextElement)
{
byte[] value;
using (var memoryStream = new MemoryStream())
{
plaintextElement.Save(memoryStream, SaveOptions.DisableFormatting);
value = memoryStream.ToArray();
}
using (var symmetricAlgorithm = DefaultSymmetricAlgorithmFactory())
{
var symmetricBlockSize = symmetricAlgorithm.BlockSize / 8;
var symmetricKey = new byte[symmetricBlockSize];
var symmetricIV = new byte[symmetricBlockSize];
_randomNumberGenerator.GetBytes(symmetricKey);
_randomNumberGenerator.GetBytes(symmetricIV);
byte[] encryptedValue;
using (var encryptor = symmetricAlgorithm.CreateEncryptor(symmetricKey, symmetricIV))
{
encryptedValue = encryptor.TransformFinalBlock(value, 0, value.Length);
}
var wrappedKey = await _client.WrapKeyAsync(_keyId, DefaultKeyEncryption, symmetricKey);
var element = new XElement("encryptedKey",
new XComment(" This key is encrypted with Azure KeyVault. "),
new XElement("kid", wrappedKey.Kid),
new XElement("key", Convert.ToBase64String(wrappedKey.Result)),
new XElement("iv", Convert.ToBase64String(symmetricIV)),
new XElement("value", Convert.ToBase64String(encryptedValue)));
return new EncryptedXmlInfo(element, typeof(AzureKeyVaultXmlDecryptor));
}
}
}
}

View File

@ -1,14 +0,0 @@
// 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.Threading.Tasks;
using Microsoft.Azure.KeyVault.Models;
namespace Microsoft.AspNetCore.DataProtection.AzureKeyVault
{
internal interface IKeyVaultWrappingClient
{
Task<KeyOperationResult> UnwrapKeyAsync(string keyIdentifier, string algorithm, byte[] cipherText);
Task<KeyOperationResult> WrapKeyAsync(string keyIdentifier, string algorithm, byte[] cipherText);
}
}

View File

@ -1,29 +0,0 @@
// 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.Threading.Tasks;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.KeyVault.Models;
namespace Microsoft.AspNetCore.DataProtection.AzureKeyVault
{
internal class KeyVaultClientWrapper : IKeyVaultWrappingClient
{
private readonly KeyVaultClient _client;
public KeyVaultClientWrapper(KeyVaultClient client)
{
_client = client;
}
public Task<KeyOperationResult> UnwrapKeyAsync(string keyIdentifier, string algorithm, byte[] cipherText)
{
return _client.UnwrapKeyAsync(keyIdentifier, algorithm, cipherText);
}
public Task<KeyOperationResult> WrapKeyAsync(string keyIdentifier, string algorithm, byte[] cipherText)
{
return _client.WrapKeyAsync(keyIdentifier, algorithm, cipherText);
}
}
}

View File

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Microsoft Azure KeyVault key encryption support.</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection;azure;keyvault</PackageTags>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.DataProtection" />
<Reference Include="Microsoft.Azure.KeyVault" />
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" />
</ItemGroup>
</Project>

View File

@ -1,9 +0,0 @@
// 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.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.DataProtection.AzureKeyVault.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

View File

@ -1,78 +0,0 @@
// 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.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.Azure.KeyVault.Models;
using Microsoft.Azure.KeyVault.WebKey;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AzureKeyVault.Test
{
public class AzureKeyVaultXmlEncryptorTests
{
[Fact]
public void UsesKeyVaultToEncryptKey()
{
var mock = new Mock<IKeyVaultWrappingClient>();
mock.Setup(client => client.WrapKeyAsync("key", JsonWebKeyEncryptionAlgorithm.RSAOAEP, It.IsAny<byte[]>()))
.Returns<string, string, byte[]>((_, __, data) => Task.FromResult(new KeyOperationResult("KeyId", data.Reverse().ToArray())));
var encryptor = new AzureKeyVaultXmlEncryptor(mock.Object, "key", new MockNumberGenerator());
var result = encryptor.Encrypt(new XElement("Element"));
var encryptedElement = result.EncryptedElement;
var value = encryptedElement.Element("value");
mock.VerifyAll();
Assert.NotNull(result);
Assert.NotNull(value);
Assert.Equal(typeof(AzureKeyVaultXmlDecryptor), result.DecryptorType);
Assert.Equal("VfLYL2prdymawfucH3Goso0zkPbQ4/GKqUsj2TRtLzsBPz7p7cL1SQaY6I29xSlsPQf6IjxHSz4sDJ427GvlLQ==", encryptedElement.Element("value").Value);
Assert.Equal("AAECAwQFBgcICQoLDA0ODw==", encryptedElement.Element("iv").Value);
Assert.Equal("Dw4NDAsKCQgHBgUEAwIBAA==", encryptedElement.Element("key").Value);
Assert.Equal("KeyId", encryptedElement.Element("kid").Value);
}
[Fact]
public void UsesKeyVaultToDecryptKey()
{
var mock = new Mock<IKeyVaultWrappingClient>();
mock.Setup(client => client.UnwrapKeyAsync("KeyId", JsonWebKeyEncryptionAlgorithm.RSAOAEP, It.IsAny<byte[]>()))
.Returns<string, string, byte[]>((_, __, data) => Task.FromResult(new KeyOperationResult(null, data.Reverse().ToArray())))
.Verifiable();
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton(mock.Object);
var encryptor = new AzureKeyVaultXmlDecryptor(serviceCollection.BuildServiceProvider());
var result = encryptor.Decrypt(XElement.Parse(
@"<encryptedKey>
<kid>KeyId</kid>
<key>Dw4NDAsKCQgHBgUEAwIBAA==</key>
<iv>AAECAwQFBgcICQoLDA0ODw==</iv>
<value>VfLYL2prdymawfucH3Goso0zkPbQ4/GKqUsj2TRtLzsBPz7p7cL1SQaY6I29xSlsPQf6IjxHSz4sDJ427GvlLQ==</value>
</encryptedKey>"));
mock.VerifyAll();
Assert.NotNull(result);
Assert.Equal("<Element />", result.ToString());
}
private class MockNumberGenerator : RandomNumberGenerator
{
public override void GetBytes(byte[] data)
{
for (int i = 0; i < data.Length; i++)
{
data[i] = (byte)i;
}
}
}
}
}

View File

@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
<CompileUsingReferenceAssemblies>false</CompileUsingReferenceAssemblies>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.DataProtection" />
<Reference Include="Microsoft.AspNetCore.DataProtection.AzureKeyVault" />
<Reference Include="Microsoft.Extensions.DependencyInjection" />
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
<Reference Include="Microsoft.AspNetCore.DataProtection" />
</ItemGroup>
</Project>

View File

@ -1,292 +0,0 @@
// 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 System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
namespace Microsoft.AspNetCore.DataProtection.AzureStorage
{
/// <summary>
/// An <see cref="IXmlRepository"/> which is backed by Azure Blob Storage.
/// </summary>
/// <remarks>
/// Instances of this type are thread-safe.
/// </remarks>
public sealed class AzureBlobXmlRepository : IXmlRepository
{
private const int ConflictMaxRetries = 5;
private static readonly TimeSpan ConflictBackoffPeriod = TimeSpan.FromMilliseconds(200);
private static readonly XName RepositoryElementName = "repository";
private readonly Func<ICloudBlob> _blobRefFactory;
private readonly Random _random;
private BlobData _cachedBlobData;
/// <summary>
/// Creates a new instance of the <see cref="AzureBlobXmlRepository"/>.
/// </summary>
/// <param name="blobRefFactory">A factory which can create <see cref="ICloudBlob"/>
/// instances. The factory must be thread-safe for invocation by multiple
/// concurrent threads, and each invocation must return a new object.</param>
public AzureBlobXmlRepository(Func<ICloudBlob> blobRefFactory)
{
_blobRefFactory = blobRefFactory ?? throw new ArgumentNullException(nameof(blobRefFactory));
_random = new Random();
}
/// <inheritdoc />
public IReadOnlyCollection<XElement> GetAllElements()
{
var blobRef = CreateFreshBlobRef();
// Shunt the work onto a ThreadPool thread so that it's independent of any
// existing sync context or other potentially deadlock-causing items.
var elements = Task.Run(() => GetAllElementsAsync(blobRef)).GetAwaiter().GetResult();
return new ReadOnlyCollection<XElement>(elements);
}
/// <inheritdoc />
public void StoreElement(XElement element, string friendlyName)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
var blobRef = CreateFreshBlobRef();
// Shunt the work onto a ThreadPool thread so that it's independent of any
// existing sync context or other potentially deadlock-causing items.
Task.Run(() => StoreElementAsync(blobRef, element)).GetAwaiter().GetResult();
}
private XDocument CreateDocumentFromBlob(byte[] blob)
{
using (var memoryStream = new MemoryStream(blob))
{
var xmlReaderSettings = new XmlReaderSettings()
{
DtdProcessing = DtdProcessing.Prohibit, IgnoreProcessingInstructions = true
};
using (var xmlReader = XmlReader.Create(memoryStream, xmlReaderSettings))
{
return XDocument.Load(xmlReader);
}
}
}
private ICloudBlob CreateFreshBlobRef()
{
// ICloudBlob instances aren't thread-safe, so we need to make sure we're working
// with a fresh instance that won't be mutated by another thread.
var blobRef = _blobRefFactory();
if (blobRef == null)
{
throw new InvalidOperationException("The ICloudBlob factory method returned null.");
}
return blobRef;
}
private async Task<IList<XElement>> GetAllElementsAsync(ICloudBlob blobRef)
{
var data = await GetLatestDataAsync(blobRef);
if (data == null)
{
// no data in blob storage
return new XElement[0];
}
// The document will look like this:
//
// <root>
// <child />
// <child />
// ...
// </root>
//
// We want to return the first-level child elements to our caller.
var doc = CreateDocumentFromBlob(data.BlobContents);
return doc.Root.Elements().ToList();
}
private async Task<BlobData> GetLatestDataAsync(ICloudBlob blobRef)
{
// Set the appropriate AccessCondition based on what we believe the latest
// file contents to be, then make the request.
var latestCachedData = Volatile.Read(ref _cachedBlobData); // local ref so field isn't mutated under our feet
var accessCondition = (latestCachedData != null)
? AccessCondition.GenerateIfNoneMatchCondition(latestCachedData.ETag)
: null;
try
{
using (var memoryStream = new MemoryStream())
{
await blobRef.DownloadToStreamAsync(
target: memoryStream,
accessCondition: accessCondition,
options: null,
operationContext: null);
// At this point, our original cache either didn't exist or was outdated.
// We'll update it now and return the updated value;
latestCachedData = new BlobData()
{
BlobContents = memoryStream.ToArray(),
ETag = blobRef.Properties.ETag
};
}
Volatile.Write(ref _cachedBlobData, latestCachedData);
}
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 304)
{
// 304 Not Modified
// Thrown when we already have the latest cached data.
// This isn't an error; we'll return our cached copy of the data.
}
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 404)
{
// 404 Not Found
// Thrown when no file exists in storage.
// This isn't an error; we'll delete our cached copy of data.
latestCachedData = null;
Volatile.Write(ref _cachedBlobData, latestCachedData);
}
return latestCachedData;
}
private int GetRandomizedBackoffPeriod()
{
// returns a TimeSpan in the range [0.8, 1.0) * ConflictBackoffPeriod
// not used for crypto purposes
var multiplier = 0.8 + (_random.NextDouble() * 0.2);
return (int) (multiplier * ConflictBackoffPeriod.Ticks);
}
private async Task StoreElementAsync(ICloudBlob blobRef, XElement element)
{
// holds the last error in case we need to rethrow it
ExceptionDispatchInfo lastError = null;
for (var i = 0; i < ConflictMaxRetries; i++)
{
if (i > 1)
{
// If multiple conflicts occurred, wait a small period of time before retrying
// the operation so that other writers can make forward progress.
await Task.Delay(GetRandomizedBackoffPeriod());
}
if (i > 0)
{
// If at least one conflict occurred, make sure we have an up-to-date
// view of the blob contents.
await GetLatestDataAsync(blobRef);
}
// Merge the new element into the document. If no document exists,
// create a new default document and inject this element into it.
var latestData = Volatile.Read(ref _cachedBlobData);
var doc = (latestData != null)
? CreateDocumentFromBlob(latestData.BlobContents)
: new XDocument(new XElement(RepositoryElementName));
doc.Root.Add(element);
// Turn this document back into a byte[].
var serializedDoc = new MemoryStream();
doc.Save(serializedDoc, SaveOptions.DisableFormatting);
// Generate the appropriate precondition header based on whether or not
// we believe data already exists in storage.
AccessCondition accessCondition;
if (latestData != null)
{
accessCondition = AccessCondition.GenerateIfMatchCondition(blobRef.Properties.ETag);
}
else
{
accessCondition = AccessCondition.GenerateIfNotExistsCondition();
blobRef.Properties.ContentType = "application/xml; charset=utf-8"; // set content type on first write
}
try
{
// Send the request up to the server.
var serializedDocAsByteArray = serializedDoc.ToArray();
await blobRef.UploadFromByteArrayAsync(
buffer: serializedDocAsByteArray,
index: 0,
count: serializedDocAsByteArray.Length,
accessCondition: accessCondition,
options: null,
operationContext: null);
// If we got this far, success!
// We can update the cached view of the remote contents.
Volatile.Write(ref _cachedBlobData, new BlobData()
{
BlobContents = serializedDocAsByteArray,
ETag = blobRef.Properties.ETag // was updated by Upload routine
});
return;
}
catch (StorageException ex)
when (ex.RequestInformation.HttpStatusCode == 409 || ex.RequestInformation.HttpStatusCode == 412)
{
// 409 Conflict
// This error is rare but can be thrown in very special circumstances,
// such as if the blob in the process of being created. We treat it
// as equivalent to 412 for the purposes of retry logic.
// 412 Precondition Failed
// We'll get this error if another writer updated the repository and we
// have an outdated view of its contents. If this occurs, we'll just
// refresh our view of the remote contents and try again up to the max
// retry limit.
lastError = ExceptionDispatchInfo.Capture(ex);
}
}
// if we got this far, something went awry
lastError.Throw();
}
private sealed class BlobData
{
internal byte[] BlobContents;
internal string ETag;
}
}
}

View File

@ -1,175 +0,0 @@
// 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.Azure.Storage;
using Microsoft.Azure.Storage.Auth;
using Microsoft.Azure.Storage.Blob;
using Microsoft.Extensions.DependencyInjection;
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;
}
}
}

View File

@ -1,24 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Microsoft Azure Blob storage support as key store.</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;dataprotection;azure;blob</PackageTags>
<IsPackable>true</IsPackable>
<UseLatestPackageReferences>true</UseLatestPackageReferences>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.DataProtection" />
<Reference Include="Microsoft.Azure.Storage.Blob" />
</ItemGroup>
<ItemGroup Condition="'$(AspNetCoreMajorMinorVersion)' == '5.0'">
<!-- This dependency was replaced by Microsoft.Azure.Storage.Blob between 3.0 and 2.2. This suppression can be removed after 3.0 is complete. -->
<SuppressBaselineReference Include="WindowsAzure.Storage" />
<Reference Include="Microsoft.Data.OData" />
</ItemGroup>
</Project>

View File

@ -1,111 +0,0 @@
// 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.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AzureStorage.Test
{
public class AzureBlobXmlRepositoryTests
{
[Fact]
public void StoreCreatesBlobWhenNotExist()
{
AccessCondition downloadCondition = null;
AccessCondition uploadCondition = null;
byte[] bytes = null;
BlobProperties properties = new BlobProperties();
var mock = new Mock<ICloudBlob>();
mock.SetupGet(c => c.Properties).Returns(properties);
mock.Setup(c => c.UploadFromByteArrayAsync(
It.IsAny<byte[]>(),
It.IsAny<int>(),
It.IsAny<int>(),
It.IsAny<AccessCondition>(),
It.IsAny<BlobRequestOptions>(),
It.IsAny<OperationContext>()))
.Returns(async (byte[] buffer, int index, int count, AccessCondition accessCondition, BlobRequestOptions options, OperationContext operationContext) =>
{
bytes = buffer.Skip(index).Take(count).ToArray();
uploadCondition = accessCondition;
await Task.Yield();
});
var repository = new AzureBlobXmlRepository(() => mock.Object);
repository.StoreElement(new XElement("Element"), null);
Assert.Null(downloadCondition);
Assert.Equal("*", uploadCondition.IfNoneMatchETag);
Assert.Equal("application/xml; charset=utf-8", properties.ContentType);
var element = "<Element />";
Assert.Equal(bytes, GetEnvelopedContent(element));
}
[Fact]
public void StoreUpdatesWhenExistsAndNewerExists()
{
AccessCondition downloadCondition = null;
byte[] bytes = null;
BlobProperties properties = new BlobProperties();
var mock = new Mock<ICloudBlob>();
mock.SetupGet(c => c.Properties).Returns(properties);
mock.Setup(c => c.DownloadToStreamAsync(
It.IsAny<Stream>(),
It.IsAny<AccessCondition>(),
null,
null))
.Returns(async (Stream target, AccessCondition condition, BlobRequestOptions options, OperationContext context) =>
{
var data = GetEnvelopedContent("<Element1 />");
await target.WriteAsync(data, 0, data.Length);
})
.Verifiable();
mock.Setup(c => c.UploadFromByteArrayAsync(
It.IsAny<byte[]>(),
It.IsAny<int>(),
It.IsAny<int>(),
It.Is((AccessCondition cond) => cond.IfNoneMatchETag == "*"),
It.IsAny<BlobRequestOptions>(),
It.IsAny<OperationContext>()))
.Throws(new StorageException(new RequestResult { HttpStatusCode = 412 }, null, null))
.Verifiable();
mock.Setup(c => c.UploadFromByteArrayAsync(
It.IsAny<byte[]>(),
It.IsAny<int>(),
It.IsAny<int>(),
It.Is((AccessCondition cond) => cond.IfNoneMatchETag != "*"),
It.IsAny<BlobRequestOptions>(),
It.IsAny<OperationContext>()))
.Returns(async (byte[] buffer, int index, int count, AccessCondition accessCondition, BlobRequestOptions options, OperationContext operationContext) =>
{
bytes = buffer.Skip(index).Take(count).ToArray();
await Task.Yield();
})
.Verifiable();
var repository = new AzureBlobXmlRepository(() => mock.Object);
repository.StoreElement(new XElement("Element2"), null);
mock.Verify();
Assert.Null(downloadCondition);
Assert.Equal(bytes, GetEnvelopedContent("<Element1 /><Element2 />"));
}
private static byte[] GetEnvelopedContent(string element)
{
return Encoding.UTF8.GetBytes($"<?xml version=\"1.0\" encoding=\"utf-8\"?><repository>{element}</repository>");
}
}
}

View File

@ -1,32 +0,0 @@
// 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.KeyManagement;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.Azure.Storage.Blob;
using Xunit;
namespace Microsoft.AspNetCore.DataProtection.AzureStorage
{
public class AzureDataProtectionBuilderExtensionsTest
{
[Fact]
public void PersistKeysToAzureBlobStorage_UsesAzureBlobXmlRepository()
{
// Arrange
var container = new CloudBlobContainer(new Uri("http://www.example.com"));
var serviceCollection = new ServiceCollection();
var builder = serviceCollection.AddDataProtection();
// Act
builder.PersistKeysToAzureBlobStorage(container, "keys.xml");
var services = serviceCollection.BuildServiceProvider();
// Assert
var options = services.GetRequiredService<IOptions<KeyManagementOptions>>();
Assert.IsType<AzureBlobXmlRepository>(options.Value.XmlRepository);
}
}
}

View File

@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseLatestPackageReferences>true</UseLatestPackageReferences>
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
<CompileUsingReferenceAssemblies>false</CompileUsingReferenceAssemblies>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.DataProtection" />
<Reference Include="Microsoft.AspNetCore.DataProtection.AzureStorage" />
<Reference Include="Microsoft.Extensions.DependencyInjection" />
<Reference Include="Microsoft.Data.OData"/>
<Reference Include="Microsoft.Data.Services.Client"/>
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
<Reference Include="Microsoft.AspNetCore.DataProtection" />
</ItemGroup>
</Project>

View File

@ -9,18 +9,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataPr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Abstractions.Tests", "Abstractions\test\Microsoft.AspNetCore.DataProtection.Abstractions.Tests.csproj", "{CEDC6CD0-0276-4C45-9B57-C222516840B9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AzureKeyVault", "AzureKeyVault", "{695602EE-F2FD-4B9B-AF84-9208AE552B9E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureKeyVault", "AzureKeyVault\src\Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj", "{DDA72406-95B8-4A67-A994-F1E5F2506126}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureKeyVault.Tests", "AzureKeyVault\test\Microsoft.AspNetCore.DataProtection.AzureKeyVault.Tests.csproj", "{0C2EE847-D78B-46FD-AB73-EAB8200C9138}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AzureStorage", "AzureStorage", "{5CD76E65-363F-4CD5-B01D-2DF5DC16CB64}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureStorage", "AzureStorage\src\Microsoft.AspNetCore.DataProtection.AzureStorage.csproj", "{22860B07-A42B-4757-9208-7D6BDB2B48F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.AzureStorage.Tests", "AzureStorage\test\Microsoft.AspNetCore.DataProtection.AzureStorage.Tests.csproj", "{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cryptography.Internal", "Cryptography.Internal", "{A5DE8834-6C9B-47A6-9CDC-AAB83C776F19}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cryptography.Internal", "Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj", "{93B3396D-0234-4D4E-A85B-4A391E0C6FE0}"
@ -53,10 +41,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataPr
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{9DF098B3-C8ED-471C-AE03-52E3196C1811}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureBlob", "samples\AzureBlob\AzureBlob.csproj", "{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureKeyVault", "samples\AzureKeyVault\AzureKeyVault.csproj", "{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomEncryptorSample", "samples\CustomEncryptorSample\CustomEncryptorSample.csproj", "{9824655A-4BBF-418E-A7D9-3DA52D98D16D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KeyManagementSample", "samples\KeyManagementSample\KeyManagementSample.csproj", "{03406538-75CB-4655-B210-643FE11A2B00}"
@ -107,54 +91,6 @@ Global
{CEDC6CD0-0276-4C45-9B57-C222516840B9}.Release|x64.Build.0 = Release|Any CPU
{CEDC6CD0-0276-4C45-9B57-C222516840B9}.Release|x86.ActiveCfg = Release|Any CPU
{CEDC6CD0-0276-4C45-9B57-C222516840B9}.Release|x86.Build.0 = Release|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Debug|x64.ActiveCfg = Debug|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Debug|x64.Build.0 = Debug|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Debug|x86.ActiveCfg = Debug|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Debug|x86.Build.0 = Debug|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Release|Any CPU.Build.0 = Release|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Release|x64.ActiveCfg = Release|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Release|x64.Build.0 = Release|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Release|x86.ActiveCfg = Release|Any CPU
{DDA72406-95B8-4A67-A994-F1E5F2506126}.Release|x86.Build.0 = Release|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Debug|x64.ActiveCfg = Debug|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Debug|x64.Build.0 = Debug|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Debug|x86.ActiveCfg = Debug|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Debug|x86.Build.0 = Debug|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Release|Any CPU.Build.0 = Release|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Release|x64.ActiveCfg = Release|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Release|x64.Build.0 = Release|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Release|x86.ActiveCfg = Release|Any CPU
{0C2EE847-D78B-46FD-AB73-EAB8200C9138}.Release|x86.Build.0 = Release|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Debug|x64.ActiveCfg = Debug|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Debug|x64.Build.0 = Debug|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Debug|x86.ActiveCfg = Debug|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Debug|x86.Build.0 = Debug|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Release|Any CPU.Build.0 = Release|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Release|x64.ActiveCfg = Release|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Release|x64.Build.0 = Release|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Release|x86.ActiveCfg = Release|Any CPU
{22860B07-A42B-4757-9208-7D6BDB2B48F3}.Release|x86.Build.0 = Release|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Debug|x64.ActiveCfg = Debug|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Debug|x64.Build.0 = Debug|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Debug|x86.ActiveCfg = Debug|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Debug|x86.Build.0 = Debug|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Release|Any CPU.Build.0 = Release|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Release|x64.ActiveCfg = Release|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Release|x64.Build.0 = Release|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Release|x86.ActiveCfg = Release|Any CPU
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9}.Release|x86.Build.0 = Release|Any CPU
{93B3396D-0234-4D4E-A85B-4A391E0C6FE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{93B3396D-0234-4D4E-A85B-4A391E0C6FE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{93B3396D-0234-4D4E-A85B-4A391E0C6FE0}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -275,30 +211,6 @@ Global
{8A71D3B4-D617-4960-8616-A196FBF351FE}.Release|x64.Build.0 = Release|Any CPU
{8A71D3B4-D617-4960-8616-A196FBF351FE}.Release|x86.ActiveCfg = Release|Any CPU
{8A71D3B4-D617-4960-8616-A196FBF351FE}.Release|x86.Build.0 = Release|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Debug|x64.ActiveCfg = Debug|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Debug|x64.Build.0 = Debug|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Debug|x86.ActiveCfg = Debug|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Debug|x86.Build.0 = Debug|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Release|Any CPU.Build.0 = Release|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Release|x64.ActiveCfg = Release|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Release|x64.Build.0 = Release|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Release|x86.ActiveCfg = Release|Any CPU
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4}.Release|x86.Build.0 = Release|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Debug|x64.ActiveCfg = Debug|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Debug|x64.Build.0 = Debug|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Debug|x86.ActiveCfg = Debug|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Debug|x86.Build.0 = Debug|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Release|Any CPU.Build.0 = Release|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Release|x64.ActiveCfg = Release|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Release|x64.Build.0 = Release|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Release|x86.ActiveCfg = Release|Any CPU
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273}.Release|x86.Build.0 = Release|Any CPU
{9824655A-4BBF-418E-A7D9-3DA52D98D16D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9824655A-4BBF-418E-A7D9-3DA52D98D16D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9824655A-4BBF-418E-A7D9-3DA52D98D16D}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -390,10 +302,6 @@ Global
GlobalSection(NestedProjects) = preSolution
{18BE6FD1-1EB1-44AD-A3AE-398C990060F5} = {ABD364B3-09A1-4CFE-8D26-FF6417DDEC84}
{CEDC6CD0-0276-4C45-9B57-C222516840B9} = {ABD364B3-09A1-4CFE-8D26-FF6417DDEC84}
{DDA72406-95B8-4A67-A994-F1E5F2506126} = {695602EE-F2FD-4B9B-AF84-9208AE552B9E}
{0C2EE847-D78B-46FD-AB73-EAB8200C9138} = {695602EE-F2FD-4B9B-AF84-9208AE552B9E}
{22860B07-A42B-4757-9208-7D6BDB2B48F3} = {5CD76E65-363F-4CD5-B01D-2DF5DC16CB64}
{C36DAB08-F641-4C5A-86C0-3CC39FC779D9} = {5CD76E65-363F-4CD5-B01D-2DF5DC16CB64}
{93B3396D-0234-4D4E-A85B-4A391E0C6FE0} = {A5DE8834-6C9B-47A6-9CDC-AAB83C776F19}
{14FD172E-4134-4712-AE77-524208FFEA1C} = {A5DE8834-6C9B-47A6-9CDC-AAB83C776F19}
{A82BAE81-24E3-48DF-96BA-C8A6FEDD274A} = {EFADD18C-A0D7-4F51-AEAB-9E3346A208BF}
@ -404,8 +312,6 @@ Global
{DC6D371D-200A-40D9-B4BE-C9202C27A863} = {361B9ACA-DCA4-4C1B-A071-906348E849C0}
{0FEAE8C5-4EAF-4E87-9A07-69FCE19B31D7} = {1A2E71DA-8DFE-4DDA-B713-14B5F2B8EBAE}
{8A71D3B4-D617-4960-8616-A196FBF351FE} = {1A2E71DA-8DFE-4DDA-B713-14B5F2B8EBAE}
{BB197CC9-04B8-4BC7-BB2E-703798B82FD4} = {9DF098B3-C8ED-471C-AE03-52E3196C1811}
{D8E4ED57-3D14-4453-A2FE-D36C97AE4273} = {9DF098B3-C8ED-471C-AE03-52E3196C1811}
{9824655A-4BBF-418E-A7D9-3DA52D98D16D} = {9DF098B3-C8ED-471C-AE03-52E3196C1811}
{03406538-75CB-4655-B210-643FE11A2B00} = {9DF098B3-C8ED-471C-AE03-52E3196C1811}
{C5C425C8-5626-409B-9A81-4DC496CE41F4} = {9DF098B3-C8ED-471C-AE03-52E3196C1811}

View File

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<OutputType>exe</OutputType>
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
<CompileUsingReferenceAssemblies>false</CompileUsingReferenceAssemblies>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.DataProtection" />
<Reference Include="Microsoft.AspNetCore.DataProtection.AzureStorage" />
<Reference Include="Microsoft.Extensions.DependencyInjection" />
<Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
<Reference Include="Microsoft.AspNetCore.DataProtection" />
</ItemGroup>
</Project>

View File

@ -1,44 +0,0 @@
// 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;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace AzureBlob
{
public class Program
{
public static void Main(string[] args)
{
var storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
var client = storageAccount.CreateCloudBlobClient();
var container = client.GetContainerReference("key-container");
// The container must exist before calling the DataProtection APIs.
// The specific file within the container does not have to exist,
// as it will be created on-demand.
container.CreateIfNotExistsAsync().GetAwaiter().GetResult();
// Configure
using (var services = new ServiceCollection()
.AddLogging(o => o.AddConsole().SetMinimumLevel(LogLevel.Debug))
.AddDataProtection()
.PersistKeysToAzureBlobStorage(container, "keys.xml")
.Services
.BuildServiceProvider())
{
// Run a sample payload
var protector = services.GetDataProtector("sample-purpose");
var protectedData = protector.Protect("Hello world!");
Console.WriteLine(protectedData);
}
}
}
}

View File

@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<OutputType>exe</OutputType>
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
<CompileUsingReferenceAssemblies>false</CompileUsingReferenceAssemblies>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.DataProtection.AzureKeyVault" />
<Reference Include="Microsoft.Extensions.Configuration" />
<Reference Include="Microsoft.Extensions.Configuration.Json" />
<Reference Include="Microsoft.Extensions.DependencyInjection" />
<Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
<Reference Include="Microsoft.AspNetCore.DataProtection" />
</ItemGroup>
</Project>

View File

@ -1,43 +0,0 @@
// 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 System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
var builder = new ConfigurationBuilder();
builder.SetBasePath(Directory.GetCurrentDirectory());
builder.AddJsonFile("settings.json");
var config = builder.Build();
var store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Find(X509FindType.FindByThumbprint, config["CertificateThumbprint"], false);
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(loggingBuilder => loggingBuilder.AddConsole());
serviceCollection.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("."))
.ProtectKeysWithAzureKeyVault(config["KeyId"], config["ClientId"], cert.OfType<X509Certificate2>().Single());
var serviceProvider = serviceCollection.BuildServiceProvider();
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var protector = serviceProvider.GetDataProtector("Test");
Console.WriteLine(protector.Protect("Hello world"));
}
}
}

View File

@ -1,5 +0,0 @@
{
"CertificateThumbprint": "",
"KeyId": "",
"ClientId": ""
}

View File

@ -26,6 +26,7 @@ namespace Templates.Test
[ConditionalFact]
[SkipOnHelix("Not supported queues", Queues = "Windows.7.Amd64;Windows.7.Amd64.Open;OSX.1014.Amd64;OSX.1014.Amd64.Open")]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]
public async Task GrpcTemplate()
{
Project = await ProjectFactory.GetOrCreateProject("grpc", Output);

View File

@ -121,6 +121,7 @@ namespace Templates.Test
[ConditionalTheory]
[MemberData(nameof(MSBuildIdentityUIPackageOptions))]
[SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]
public async Task IdentityUIPackage_WorksWithDifferentOptions(IDictionary<string, string> packageOptions, string versionValidator, string[] expectedFiles)
{
Project = await ProjectFactory.GetOrCreateProject("identityuipackage" + string.Concat(packageOptions.Values), Output);

View File

@ -108,6 +108,7 @@ namespace Templates.Test
[InlineData(true)]
[InlineData(false)]
[SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]
public async Task MvcTemplate_IndividualAuth(bool useLocalDB)
{
Project = await ProjectFactory.GetOrCreateProject("mvcindividual" + (useLocalDB ? "uld" : ""), Output);
@ -223,6 +224,7 @@ namespace Templates.Test
}
[Fact]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]
public async Task MvcTemplate_RazorRuntimeCompilation_BuildsAndPublishes()
{
Project = await ProjectFactory.GetOrCreateProject("mvc_rc", Output);

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing;
using Templates.Test.Helpers;
using Xunit;
using Xunit.Abstractions;
@ -41,6 +42,7 @@ namespace Templates.Test
}
[Fact]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]
public async Task RazorClassLibraryTemplateAsync()
{
Project = await ProjectFactory.GetOrCreateProject("razorclasslib", Output);

View File

@ -97,7 +97,8 @@ namespace Templates.Test
[ConditionalTheory]
[InlineData(false)]
[InlineData(true)]
[SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]
[SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]
public async Task RazorPagesTemplate_IndividualAuth(bool useLocalDB)
{
Project = await ProjectFactory.GetOrCreateProject("razorpagesindividual" + (useLocalDB ? "uld" : ""), Output);
@ -213,6 +214,7 @@ namespace Templates.Test
}
[Fact]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]
public async Task RazorPagesTemplate_RazorRuntimeCompilation_BuildsAndPublishes()
{
Project = await ProjectFactory.GetOrCreateProject("razorpages_rc", Output);

View File

@ -22,6 +22,7 @@ namespace Templates.Test
public ITestOutputHelper Output { get; }
[Fact]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]
public async Task WorkerTemplateAsync()
{
Project = await ProjectFactory.GetOrCreateProject("worker", Output);

View File

@ -6,6 +6,7 @@
<TestGroupName>Interop.FunctionalTests</TestGroupName>
<!-- WebDriver is not strong named, so this test assembly cannot be strong-named either. -->
<SignAssembly>false</SignAssembly>
<BuildHelixPayload Condition="'$(TargetArchitecture)' == 'arm64'">false</BuildHelixPayload>
</PropertyGroup>
<ItemGroup>