diff --git a/src/SecretManager/Microsoft.DncEng.SecretManager.ScenarioTests/StorageTests.cs b/src/SecretManager/Microsoft.DncEng.SecretManager.ScenarioTests/StorageTests.cs deleted file mode 100644 index 5069a1c47..000000000 --- a/src/SecretManager/Microsoft.DncEng.SecretManager.ScenarioTests/StorageTests.cs +++ /dev/null @@ -1,229 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Azure; -using Azure.Core; -using Azure.ResourceManager.Storage; -using Azure.ResourceManager.Storage.Models; -using Azure.Security.KeyVault.Secrets; -using NUnit.Framework; - -namespace Microsoft.DncEng.SecretManager.Tests -{ - [TestFixture] - [Category("PostDeployment")] - public class StorageTests : ScenarioTestsBase - { - const string AccountName = "secretmanagertests"; - const string ConnectionStringNamePrefix = "azure-storage-connection-string"; - const string BlobSasNamePrefix = "azure-storage-blob-sas-uri"; - const string TableSasNamePrefix = "azure-storage-table-sas-uri"; - const string ContainerSasUriNamePrefix = "azure-storage-container-sas-uri"; - const string ContainerSasTokenNamePrefix = "azure-storage-container-sas-token"; - const string StorageSasUriTokenNamePrefix = "azure-storage-account-sas-uri"; - const string KeyNamePrefix = "azure-storage-key"; - const string AccountKeyPrefix = "AccountKey="; - - readonly string Manifest = @$"storageLocation: - type: azure-key-vault - parameters: - name: {KeyVaultName} - subscription: {SubscriptionId} -secrets: - {KeyNamePrefix}{{0}}: - type: azure-storage-key - owner: scenarioTests - description: storage key - parameters: - Subscription: {SubscriptionId} - Account: {AccountName} - {ConnectionStringNamePrefix}{{0}}: - type: azure-storage-connection-string - owner: scenarioTests - description: storage connection string - parameters: - StorageKeySecret: {KeyNamePrefix}{{0}} - Account: {AccountName} - {BlobSasNamePrefix}{{0}}: - type: azure-storage-blob-sas-uri - owner: scenarioTests - description: blob sas - parameters: - ConnectionString: {ConnectionStringNamePrefix}{{0}} - Container: test - Blob: test.txt - Permissions: r - {TableSasNamePrefix}{{0}}: - type: azure-storage-table-sas-uri - owner: scenarioTests - description: table sas - parameters: - ConnectionString: {ConnectionStringNamePrefix}{{0}} - Table: testTable - Permissions: r - {ContainerSasUriNamePrefix}{{0}}: - type: azure-storage-container-sas-uri - owner: scenarioTests - description: container sas uri - parameters: - ConnectionString: {ConnectionStringNamePrefix}{{0}} - Container: test - Permissions: lr - {ContainerSasTokenNamePrefix}{{0}}: - type: azure-storage-container-sas-token - owner: scenarioTests - description: container sas token - parameters: - ConnectionString: {ConnectionStringNamePrefix}{{0}} - Container: test - Permissions: l - {StorageSasUriTokenNamePrefix}{{0}}: - type: azure-storage-account-sas-uri - owner: scenarioTests - description: storage account sas URI - parameters: - ConnectionString: {ConnectionStringNamePrefix}{{0}} - Service: blob - Permissions: l"; - - - [Test] - public async Task NewStorageSecretsTest() - { - string nameSuffix = Guid.NewGuid().ToString("N"); - string keySecretName = KeyNamePrefix + nameSuffix; - string connectionStringSecretName = ConnectionStringNamePrefix + nameSuffix; - string blobSasSecretName = BlobSasNamePrefix + nameSuffix; - string tableSasSecretName = TableSasNamePrefix + nameSuffix; - string containerSasUriSecretName = ContainerSasUriNamePrefix + nameSuffix; - string containerSasTokenSecretName = ContainerSasTokenNamePrefix + nameSuffix; - string storageSasUriTokenSecretName = StorageSasUriTokenNamePrefix + nameSuffix; - string manifest = string.Format(Manifest, nameSuffix); - - await ExecuteSynchronizeCommand(manifest); - - SecretClient client = GetSecretClient(); - Response keySecret = await client.GetSecretAsync(keySecretName); - Response connectionStringSecret = await client.GetSecretAsync(connectionStringSecretName); - - HashSet connectionStringAccessKeys = await GetAccessKeys(); - - string extractedAccountKey = ExtractKeyFromConnectionString(connectionStringSecret.Value); - Assert.That(connectionStringAccessKeys, Contains.Item(extractedAccountKey)); - Assert.That(keySecret.Value.Value, Is.EqualTo(extractedAccountKey)); - - Response blobSasSecret = await client.GetSecretAsync(blobSasSecretName); - AssertValidSasUri(blobSasSecret.Value.Value); - Response tableSasSecret = await client.GetSecretAsync(tableSasSecretName); - AssertValidSasUri(tableSasSecret.Value.Value); - Response containerSasUriSecret = await client.GetSecretAsync(containerSasUriSecretName); - AssertValidSasUri(containerSasUriSecret.Value.Value); - Response containerSasTokenSecret = await client.GetSecretAsync(containerSasTokenSecretName); - AssertValidSas(containerSasTokenSecret.Value.Value); - Response storageSasUriTokenSecret = await client.GetSecretAsync(storageSasUriTokenSecretName); - AssertValidSas(storageSasUriTokenSecret.Value.Value); - } - - [Test] - public async Task RotateSecretTest() - { - string nameSuffix = Guid.NewGuid().ToString("N"); - string keySecretName = KeyNamePrefix + nameSuffix; - string connectionStringSecretName = ConnectionStringNamePrefix + nameSuffix; - string blobSasSecretName = BlobSasNamePrefix + nameSuffix; - string tableSasSecretName = TableSasNamePrefix + nameSuffix; - string containerSasUriSecretName = ContainerSasUriNamePrefix + nameSuffix; - string containerSasTokenSecretName = ContainerSasTokenNamePrefix + nameSuffix; - string storageSasUriTokenSecretName = StorageSasUriTokenNamePrefix + nameSuffix; - string manifest = string.Format(Manifest, nameSuffix); - - SecretClient client = GetSecretClient(); - - Response keySecret = await client.SetSecretAsync(keySecretName, "TEST"); - await UpdateNextRotationTagIntoPast(client, keySecret.Value); - Response connectionStringSecret = await client.SetSecretAsync(connectionStringSecretName, "TEST"); - await UpdateNextRotationTagIntoPast(client, connectionStringSecret.Value); - Response blobSasSecret = await client.SetSecretAsync(blobSasSecretName, "TEST"); - await UpdateNextRotationTagIntoFuture(client, blobSasSecret.Value); - Response tableSasSecret = await client.SetSecretAsync(tableSasSecretName, "TEST"); - await UpdateNextRotationTagIntoFuture(client, tableSasSecret.Value); - Response containerSasUriSecret = await client.SetSecretAsync(containerSasUriSecretName, "TEST"); - await UpdateNextRotationTagIntoFuture(client, containerSasUriSecret.Value); - Response containerSasTokenSecret = await client.SetSecretAsync(containerSasTokenSecretName, "TEST"); - await UpdateNextRotationTagIntoFuture(client, containerSasTokenSecret.Value); - Response storageSasUriTokenSecret = await client.SetSecretAsync(storageSasUriTokenSecretName, "TEST"); - await UpdateNextRotationTagIntoFuture(client, storageSasUriTokenSecret.Value); - - - HashSet accessKeys = await GetAccessKeys(); - - await ExecuteSynchronizeCommand(manifest); - - HashSet accessKeysRotated = await GetAccessKeys(); - - accessKeysRotated.ExceptWith(accessKeys); - - - Assert.That(accessKeysRotated, Has.Count.EqualTo(1)); - var rotatedAccountKey = accessKeysRotated.First(); - - keySecret = await client.GetSecretAsync(keySecretName); - connectionStringSecret = await client.GetSecretAsync(connectionStringSecretName); - string accountKeyFromConnectionString = ExtractKeyFromConnectionString(connectionStringSecret.Value); - - Assert.That(rotatedAccountKey, Is.EqualTo(accountKeyFromConnectionString)); - Assert.That(keySecret.Value.Value, Is.EqualTo(accountKeyFromConnectionString)); - - blobSasSecret = await client.GetSecretAsync(blobSasSecretName); - AssertValidSasUri(blobSasSecret.Value.Value); - tableSasSecret = await client.GetSecretAsync(tableSasSecretName); - AssertValidSasUri(tableSasSecret.Value.Value); - containerSasUriSecret = await client.GetSecretAsync(containerSasUriSecretName); - AssertValidSasUri(containerSasUriSecret.Value.Value); - containerSasTokenSecret = await client.GetSecretAsync(containerSasTokenSecretName); - AssertValidSas(containerSasTokenSecret.Value.Value); - storageSasUriTokenSecret = await client.GetSecretAsync(storageSasUriTokenSecretName); - AssertValidSas(storageSasUriTokenSecret.Value.Value); - } - - [OneTimeTearDown] - public async Task Cleanup() - { - await PurgeAllSecrets(); - } - - private static void AssertValidSasUri(string uriText) - { - var uri = new Uri(uriText); - AssertValidSas(uri.Query); - } - - private static void AssertValidSas(string sas) - { - var query = System.Web.HttpUtility.ParseQueryString(sas); - Assert.That(query["sig"], Is.Not.Null); - } - - private static string ExtractKeyFromConnectionString(KeyVaultSecret secret) - { - if (secret == null || secret.Value == null) - return null; - - var accountKey = secret.Value.Split(';').FirstOrDefault(l => l.StartsWith(AccountKeyPrefix)); - if (accountKey == null) - return null; - - return accountKey.Substring(AccountKeyPrefix.Length); - } - - private async Task> GetAccessKeys() - { - ResourceIdentifier storageAccountResourceId = StorageAccountResource.CreateResourceIdentifier(SubscriptionId, ResourceGroup, AccountName); - StorageAccountResource storageAccount = _armClient.GetStorageAccountResource(storageAccountResourceId); - List result = await storageAccount.GetKeysAsync().ToListAsync(); - - return result.Select(l => l.Value).ToHashSet(); - } - } -} diff --git a/src/SecretManager/Microsoft.DncEng.SecretManager.Tests/StorageUtilsTests.cs b/src/SecretManager/Microsoft.DncEng.SecretManager.Tests/StorageUtilsTests.cs deleted file mode 100644 index dcde08bdd..000000000 --- a/src/SecretManager/Microsoft.DncEng.SecretManager.Tests/StorageUtilsTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -using AwesomeAssertions; -using Moq; -using NUnit.Framework; -using System; - -namespace Microsoft.DncEng.SecretManager.Tests; - -#nullable enable - -public class StorageUtilsTests -{ - [TestCase("AccountName=PlaceholderAccountName", "PlaceholderAccountName")] - [TestCase("AccountName=PlaceholderAccountName;AccountKey=Placeholder", "PlaceholderAccountName")] - [TestCase("DefaultEndpointsProtocol=https;AccountName=PlaceholderAccountName;AccountKey=placeholder;BlobEndpoint=https://placeholder.blob.core.windows.net/;QueueEndpoint=https://placeholder.queue.core.windows.net/;TableEndpoint=https://placeholder.table.core.windows.net/;FileEndpoint=https://placeholder.file.core.windows.net/;", "PlaceholderAccountName")] - public void CanParseAccountNameFromValidStrings(string testConnectionString, string expectedName) - { - bool actualResult = StorageUtils.TryParseStorageConnectionStringAccountName(testConnectionString, out string? actualName); - - actualResult.Should().BeTrue(); - expectedName.Should().Be(actualName); - } - - [TestCase("AccountKey=PlaceholderAccountKey", "PlaceholderAccountKey")] - [TestCase("AccountName=PlaceholderAccountName;AccountKey=PlaceholderAccountKey", "PlaceholderAccountKey")] - [TestCase("DefaultEndpointsProtocol=https;AccountName=PlaceholderAccountName;AccountKey=PlaceholderAccountKey;BlobEndpoint=https://placeholder.blob.core.windows.net/;QueueEndpoint=https://placeholder.queue.core.windows.net/;TableEndpoint=https://placeholder.table.core.windows.net/;FileEndpoint=https://placeholder.file.core.windows.net/;", "PlaceholderAccountKey")] - [TestCase("DefaultEndpointsProtocol=https;AccountName=PlaceholderAccountName;AccountKey=PlaceholderAccountKey", "PlaceholderAccountKey")] - public void CanParseAccountKeyFromValidStrings(string testConnectionString, string expectedKey) - { - bool actualResult = StorageUtils.TryParseStorageConnectionStringAccountKey(testConnectionString, out string? actualKey); - - actualResult.Should().BeTrue(); - expectedKey.Should().Be(actualKey); - } - - [TestCase(" ")] - [TestCase("asdf")] - [TestCase("AccountName=")] - [TestCase("AccountName=;AccountKey=")] - public void DoNotThrowFromInvalidString(string testConnectionString) - { - bool? actualResult = null; - - Action act = () => actualResult = StorageUtils.TryParseStorageConnectionStringAccountName(testConnectionString, out string? _); - - act.Should().NotThrow(); - actualResult.Should().BeFalse(); - } - - [Test] - public void GenerateBlobContainerSasPrependsQueryStringSeparator() - { - string testConnectionString = "DefaultEndpointsProtocol=https;AccountName=PlaceholderAccountName;AccountKey=aaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabPLACEHOLDER"; - - string testContainerName = "PlaceholderContainerName"; - string testPermissionsString = "l"; - DateTimeOffset testExpiresOn = DateTimeOffset.UtcNow.AddMonths(1); - - (string _, string actualSas) = StorageUtils.GenerateBlobContainerSas(testConnectionString, testContainerName, testPermissionsString, testExpiresOn); - - actualSas.Should().StartWith("?", "the query string separator is required for compatibility with standard set by Microsoft.WindowsAzure.Storage"); - } - - [Test] - public void GenerateBlobAccountSasPrependsQueryStringSeparator() - { - string testConnectionString = "DefaultEndpointsProtocol=https;AccountName=PlaceholderAccountName;AccountKey=aaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabPLACEHOLDER"; - - string testPermissionsString = "rl"; - string testServiceName = "blob"; - DateTimeOffset testExpiresOn = DateTimeOffset.UtcNow.AddMonths(1); - - (string _, string actualSas) = StorageUtils.GenerateBlobAccountSas(testConnectionString, testPermissionsString, testServiceName, testExpiresOn); - - actualSas.Should().StartWith("?", "the query string separator is required for compatibility with standard set by Microsoft.WindowsAzure.Storage"); - } -}