Skip to content

Commit

Permalink
[Internal] Client Encryption : Adds test to verify that update of Cli…
Browse files Browse the repository at this point in the history
…ent Encryption Policy is not allowed via ReplaceContainer (#2349)

Adds / updates existing tests to verify -
a. Update of Client Encryption Policy is not allowed via ReplaceContainer
b. CreateContainer request ensures that the ClientEncryptionKey exists when creating Client Encryption Policy
  • Loading branch information
anujtoshniwal authored and ealsur committed Jun 14, 2021
1 parent 90320a1 commit 68271cf
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -362,36 +362,6 @@ public async Task EncryptionResourceTokenAuthRestricted()
}
}

[TestMethod]
public async Task EncryptionFailsWithUnknownClientEncryptionKey()
{
ClientEncryptionIncludedPath unknownKeyConfigured = new ClientEncryptionIncludedPath()
{
Path = "/",
ClientEncryptionKeyId = "unknownKey",
EncryptionType = "Deterministic",
EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256",
};

Collection<ClientEncryptionIncludedPath> paths = new Collection<ClientEncryptionIncludedPath> { unknownKeyConfigured };
ClientEncryptionPolicy clientEncryptionPolicyId = new ClientEncryptionPolicy(paths);

ContainerProperties containerProperties = new ContainerProperties(Guid.NewGuid().ToString(), "/PK") { ClientEncryptionPolicy = clientEncryptionPolicyId };

Container encryptionContainer = await database.CreateContainerAsync(containerProperties, 400);

try
{
await encryptionContainer.InitializeEncryptionAsync();
await MdeEncryptionTests.MdeCreateItemAsync(encryptionContainer);
Assert.Fail("Expected item creation should fail since client encryption policy is configured with unknown key.");
}
catch (Exception ex)
{
Assert.IsTrue(ex is InvalidOperationException);
}
}

[TestMethod]
public async Task ClientEncryptionPolicyTests()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1320,9 +1320,41 @@ public async Task TimeToLivePropertyPath()
Assert.AreEqual(HttpStatusCode.NoContent, containerResponse.StatusCode);
}

[TestMethod]
public async Task ContainerCreationFailsWithUnknownClientEncryptionKey()
{
ClientEncryptionIncludedPath unknownKeyConfigured = new ClientEncryptionIncludedPath()
{
Path = "/",
ClientEncryptionKeyId = "unknownKey",
EncryptionType = "Deterministic",
EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256",
};

Collection<ClientEncryptionIncludedPath> paths = new Collection<ClientEncryptionIncludedPath> { unknownKeyConfigured };
ClientEncryptionPolicy clientEncryptionPolicyId = new ClientEncryptionPolicy(paths);

ContainerProperties containerProperties = new ContainerProperties(Guid.NewGuid().ToString(), "/PK") { ClientEncryptionPolicy = clientEncryptionPolicyId };

try
{
await this.cosmosDatabase.CreateContainerAsync(containerProperties, 400);
Assert.Fail("Expected container creation should fail since client encryption policy is configured with unknown key.");
}
catch (CosmosException ex)
{
Assert.AreEqual(HttpStatusCode.BadRequest, ex.StatusCode);
Assert.IsTrue(ex.Message.Contains("ClientEncryptionKey with id '[unknownKey]' does not exist."));
}
}

[TestMethod]
public async Task ClientEncryptionPolicyTest()
{
DatabaseInlineCore databaseInlineCore = (DatabaseInlineCore)this.cosmosDatabase;
await TestCommon.CreateClientEncryptionKey("dekId1", databaseInlineCore);
await TestCommon.CreateClientEncryptionKey("dekId2", databaseInlineCore);

string containerName = Guid.NewGuid().ToString();
string partitionKeyPath = "/users";
Collection<ClientEncryptionIncludedPath> paths = new Collection<ClientEncryptionIncludedPath>()
Expand Down Expand Up @@ -1371,6 +1403,32 @@ public async Task ClientEncryptionPolicyTest()
ContainerResponse readResponse = await container.ReadContainerAsync();
Assert.AreEqual(HttpStatusCode.Created, containerResponse.StatusCode);
Assert.IsNotNull(readResponse.Resource.ClientEncryptionPolicy);

// replace without updating CEP should be successful
readResponse.Resource.IndexingPolicy = new Cosmos.IndexingPolicy()
{
IndexingMode = Cosmos.IndexingMode.None,
Automatic = false
};

containerResponse = await container.ReplaceContainerAsync(readResponse.Resource);
Assert.AreEqual(HttpStatusCode.OK, containerResponse.StatusCode);
Assert.AreEqual(Cosmos.IndexingMode.None, containerResponse.Resource.IndexingPolicy.IndexingMode);
Assert.IsFalse(containerResponse.Resource.IndexingPolicy.Automatic);

// update CEP and attempt replace
readResponse.Resource.ClientEncryptionPolicy = null;
try
{
await container.ReplaceContainerAsync(readResponse.Resource);

Assert.Fail("ReplaceCollection with update to ClientEncryptionPolicy should have failed.");
}
catch (CosmosException ex)
{
Assert.AreEqual(HttpStatusCode.BadRequest, ex.StatusCode);
Assert.IsTrue(ex.Message.Contains("'clientEncryptionPolicy' cannot be changed as part of collection replace operation."));
}
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ public async Task Cleanup()
[TestMethod]
public async Task ContainerContractTest()
{
DatabaseInlineCore databaseInlineCore = (DatabaseInlineCore)this.database;
await TestCommon.CreateClientEncryptionKey("dekId", databaseInlineCore);

ClientEncryptionIncludedPath clientEncryptionIncludedPath1 = new ClientEncryptionIncludedPath()
{
Path = "/path",
Expand Down Expand Up @@ -558,7 +561,7 @@ public async Task TimeToLivePropertyPath()
containerResponse = await this.database.DefineContainer(containerName, partitionKeyPath)
.WithTimeToLivePropertyPath("/creationDate")
.CreateAsync();
Assert.Fail("CreateColleciton with TtlPropertyPath and with no DefaultTimeToLive should have failed.");
Assert.Fail("CreateCollection with TtlPropertyPath and with no DefaultTimeToLive should have failed.");
}
catch (CosmosException exeption)
{
Expand Down Expand Up @@ -593,20 +596,25 @@ public async Task TimeToLivePropertyPath()
[TestMethod]
public async Task WithClientEncryptionPolicyTest()
{
// create ClientEncryptionKeys
DatabaseInlineCore databaseInlineCore = (DatabaseInlineCore)this.database;
await TestCommon.CreateClientEncryptionKey("dekId1", databaseInlineCore);
await TestCommon.CreateClientEncryptionKey("dekId2", databaseInlineCore);

string containerName = Guid.NewGuid().ToString();
string partitionKeyPath = "/users";
ClientEncryptionIncludedPath path1 = new ClientEncryptionIncludedPath()
{
Path = "/path1",
ClientEncryptionKeyId = "key1",
ClientEncryptionKeyId = "dekId1",
EncryptionType = "Randomized",
EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256"
};

ClientEncryptionIncludedPath path2 = new ClientEncryptionIncludedPath()
{
Path = "/path2",
ClientEncryptionKeyId = "key2",
ClientEncryptionKeyId = "dekId2",
EncryptionType = "Randomized",
EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256",
};
Expand All @@ -632,6 +640,20 @@ public async Task WithClientEncryptionPolicyTest()
ContainerResponse readResponse = await container.ReadContainerAsync();
Assert.AreEqual(HttpStatusCode.Created, containerResponse.StatusCode);
Assert.IsNotNull(readResponse.Resource.ClientEncryptionPolicy);

// update CEP and replace container
readResponse.Resource.ClientEncryptionPolicy = null;
try
{
await container.ReplaceContainerAsync(readResponse.Resource);

Assert.Fail("ReplaceCollection with update to ClientEncryptionPolicy should have failed.");
}
catch (CosmosException ex)
{
Assert.AreEqual(HttpStatusCode.BadRequest, ex.StatusCode);
Assert.IsTrue(ex.Message.Contains("'clientEncryptionPolicy' cannot be changed as part of collection replace operation."));
}
}

[TestMethod]
Expand All @@ -655,7 +677,7 @@ public async Task WithClientEncryptionPolicyFailureTest()
.Attach()
.CreateAsync();

Assert.Fail("CreateColleciton with invalid ClientEncryptionPolicy should have failed.");
Assert.Fail("CreateCollection with invalid ClientEncryptionPolicy should have failed.");
}
catch (ArgumentNullException ex)
{
Expand All @@ -673,7 +695,7 @@ public async Task WithClientEncryptionPolicyFailureTest()
.Attach()
.CreateAsync();

Assert.Fail("CreateColleciton with invalid ClientEncryptionPolicy should have failed.");
Assert.Fail("CreateCollection with invalid ClientEncryptionPolicy should have failed.");
}
catch (ArgumentException ex)
{
Expand All @@ -691,7 +713,7 @@ public async Task WithClientEncryptionPolicyFailureTest()
.Attach()
.CreateAsync();

Assert.Fail("CreateColleciton with invalid ClientEncryptionPolicy should have failed.");
Assert.Fail("CreateCollection with invalid ClientEncryptionPolicy should have failed.");
}
catch (ArgumentException ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -341,6 +342,41 @@ internal static void RouteToTheOnlyPartition(DocumentClient client, DocumentServ
request.RouteTo(new PartitionKeyRangeIdentity(collection.ResourceId, ranges.Single().Id));
}

internal static async Task CreateClientEncryptionKey(
string dekId,
DatabaseInlineCore databaseInlineCore)
{
EncryptionKeyWrapMetadata metadata = new EncryptionKeyWrapMetadata("custom", dekId, "tempMetadata");

byte[] wrappedDataEncryptionKey = new byte[32];
// Generate random bytes cryptographically.
using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider())
{
rngCsp.GetBytes(wrappedDataEncryptionKey);
}

ClientEncryptionKeyProperties clientEncryptionKeyProperties = new ClientEncryptionKeyProperties(
dekId,
"AEAD_AES_256_CBC_HMAC_SHA256",
wrappedDataEncryptionKey,
metadata);

try
{
await databaseInlineCore.CreateClientEncryptionKeyAsync(clientEncryptionKeyProperties);
}
catch(CosmosException ex)
{
if (ex.StatusCode == HttpStatusCode.Conflict &&
ex.Message.Contains("Resource with specified id, name, or unique index already exists."))
{
return;
}

throw;
}
}

internal static Database CreateOrGetDatabase(DocumentClient client)
{
IList<Database> databases = TestCommon.ListAll<Database>(
Expand Down

0 comments on commit 68271cf

Please sign in to comment.