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:
commit
c4305ff51e
|
|
@ -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)" />
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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")]
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"CertificateThumbprint": "",
|
||||
"KeyId": "",
|
||||
"ClientId": ""
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue