Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CosmosDB] Adds support to create containers with Client Encryption Policy #18034

Merged
merged 13 commits into from
May 13, 2022
Merged
4 changes: 2 additions & 2 deletions src/CosmosDB/CosmosDB.Test/CosmosDB.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Import Project="$(MSBuildThisFileDirectory)..\..\Az.Test.props" />
<ItemGroup>
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.1" />
<PackageReference Include="Microsoft.Azure.Management.CosmosDB" Version="3.5.0-preview" />
<PackageReference Include="Microsoft.Azure.Management.CosmosDB" Version="3.7.0-preview" />
<PackageReference Include="Microsoft.Azure.Management.Network" Version="22.0.0" />
</ItemGroup>
</Project>
</Project>
34 changes: 34 additions & 0 deletions src/CosmosDB/CosmosDB.Test/ScenarioTests/SqlOperationsTests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,8 @@ function Test-ClientEncryptionKeyCmdlets
$DatabaseName = "dbNameCdbAE"
$ClientEncryptionKeyName = "cek1"
$EncryptionAlgorithm = "AEAD_AES_256_CBC_HMAC_SHA256"
$EncryptionType_1 = "Deterministic"
$EncryptionType_2 = "Randomized"
$keywrapmetadataName = "cmk1v1"
$keywrapmetadataName2 = "cmk1v2"
$keywrapmetadataType = "AZURE_KEY_VAULT"
Expand Down Expand Up @@ -932,6 +934,7 @@ function Test-ClientEncryptionKeyCmdlets
Remove-AzKeyVault -VaultName $vaultName -InRemovedState -Force -Location $location
}
catch{}

$encryptionKeyVault=New-AzKeyVault -VaultName $vaultName -ResourceGroupName $rgName -Location $location

# add access police for key-vault
Expand Down Expand Up @@ -985,6 +988,37 @@ function Test-ClientEncryptionKeyCmdlets
Assert-AreEqual $UpdatedClientEncryptionKey.Resource.keyWrapMetadata.type $keywrapmetadataType
Assert-AreEqual $UpdatedClientEncryptionKey.Resource.keyWrapMetadata.value $encryptionKey2
Assert-AreEqual $UpdatedClientEncryptionKey.Resource.keyWrapMetadata.algorithm $keywrapmetadataAlgo

#Test - validate client encryption policy creation.
$includedPath_1 = [Microsoft.Azure.Management.CosmosDB.Models.ClientEncryptionIncludedPath]::new("/path1",$ClientEncryptionKeyName,$EncryptionType_1,$EncryptionAlgorithm);
$includedPath_2 = [Microsoft.Azure.Management.CosmosDB.Models.ClientEncryptionIncludedPath]::new("/path2",$ClientEncryptionKeyName,$EncryptionType_2,$EncryptionAlgorithm);
$listofIncludedPaths = New-Object Collections.Generic.List[Microsoft.Azure.Management.CosmosDB.Models.ClientEncryptionIncludedPath]
$listofIncludedPaths.Add($includedPath_1)
$listofIncludedPaths.Add($includedPath_2)
$newClientEncryptionPolicy = New-Object Microsoft.Azure.Management.CosmosDB.Models.ClientEncryptionPolicy
$newClientEncryptionPolicy.IncludedPaths = $listofIncludedPaths
#verify the default policy version 1 is picked up
$newClientEncryptionPolicy.PolicyFormatVersion = 187
$newPSSqlClientEncryptionPolicy = [Microsoft.Azure.Commands.CosmosDB.Models.PSSqlClientEncryptionPolicy]::new($newClientEncryptionPolicy)

$ContainerWithEncryptionPolicy = "containerWithEncryptionPolicy"
#create a container with the above policy
New-AzCosmosDBSqlContainer -AccountName $AccountName -ResourceGroupName $rgName -DatabaseName $DatabaseName -Name $ContainerWithEncryptionPolicy -PartitionKeyPath "/pk" -PartitionKeyKind Hash -ClientEncryptionPolicy $newPSSqlClientEncryptionPolicy

$ContainerWithEncryptionPolicy = Get-AzCosmosDBSqlContainer -AccountName $AccountName -ResourceGroupName $rgName -DatabaseName $DatabaseName -Name $ContainerWithEncryptionPolicy

Assert-AreEqual $ContainerWithEncryptionPolicy.Resource.ClientEncryptionPolicy.IncludedPaths[0].Path "/path1"
Assert-AreEqual $ContainerWithEncryptionPolicy.Resource.ClientEncryptionPolicy.IncludedPaths[0].ClientEncryptionKeyId $ClientEncryptionKeyName
Assert-AreEqual $ContainerWithEncryptionPolicy.Resource.ClientEncryptionPolicy.IncludedPaths[0].EncryptionAlgorithm $EncryptionAlgorithm
Assert-AreEqual $ContainerWithEncryptionPolicy.Resource.ClientEncryptionPolicy.IncludedPaths[0].EncryptionType $EncryptionType_1
Assert-AreEqual $ContainerWithEncryptionPolicy.Resource.ClientEncryptionPolicy.PolicyFormatVersion 1

Assert-AreEqual $ContainerWithEncryptionPolicy.Resource.ClientEncryptionPolicy.IncludedPaths[1].Path "/path2"
Assert-AreEqual $ContainerWithEncryptionPolicy.Resource.ClientEncryptionPolicy.IncludedPaths[1].ClientEncryptionKeyId $ClientEncryptionKeyName
Assert-AreEqual $ContainerWithEncryptionPolicy.Resource.ClientEncryptionPolicy.IncludedPaths[1].EncryptionAlgorithm $EncryptionAlgorithm
Assert-AreEqual $ContainerWithEncryptionPolicy.Resource.ClientEncryptionPolicy.IncludedPaths[1].EncryptionType $EncryptionType_2
Assert-AreEqual $ContainerWithEncryptionPolicy.Resource.ClientEncryptionPolicy.PolicyFormatVersion 1

}
Finally {
Remove-AzCosmosDBSqlDatabase -AccountName $AccountName -ResourceGroupName $rgName -Name $DatabaseName
Expand Down
1 change: 1 addition & 0 deletions src/CosmosDB/CosmosDB/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
-->

## Upcoming Release
* Introduced support for creating containers with Client Encryption Policy. The current supported version of Client Encryption Policy is 1.

## Version 1.7.0
* Introduced support for client encryption key resource management required for CosmosDB Client-Side Encryption by adding support for creating, updating and retrieving client encryption keys with following cmdlets: `Get-AzCosmosDbClientEncryptionKey`, `New-AzCosmosDbClientEncryptionKey` and `Update-AzCosmosDbClientEncryptionKey`
Expand Down
2 changes: 1 addition & 1 deletion src/CosmosDB/CosmosDB/CosmosDB.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Import Project="$(MSBuildThisFileDirectory)..\..\Az.props" />
<ItemGroup>
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0-beta.4" />
<PackageReference Include="Microsoft.Azure.Management.CosmosDB" Version="3.5.0-preview" />
<PackageReference Include="Microsoft.Azure.Management.CosmosDB" Version="3.7.0-preview" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions src/CosmosDB/CosmosDB/Helpers/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ internal static class Constants
public const string SqlUniqueKeyPolciyHelpMessage = "UniqueKeyPolicy Object of type Microsoft.Azure.Commands.CosmosDB.PSSqlUniqueKeyPolicy. ";
public const string UniqueKeyPolciyHelpMessage = "UniqueKeyPolicy Object of type Microsoft.Azure.Commands.CosmosDB.PSUniqueKeyPolicy. ";
public const string SqlConflictResolutionPolicyHelpMessage = "ConflictResolutionPolicy Object of type PSSqlConflictResolutionPolicy, when provided this is set as the ConflictResolutionPolicy of the container.";
public const string SqlClientEncryptionPolicyHelpMessage = "ClientEncryptionPolicy Object of type PSSqlClientEncryptionPolicy, when provided this is set as the ClientEncryptionPolicy of the container.";
public const string ConflictResolutionPolicyHelpMessage = "ConflictResolutionPolicy Object of type PSConflictResolutionPolicy, when provided this is set as the ConflictResolutionPolicy of the container.";
public const string PartitionKeyPathHelpMessage = "Partition Key Path, e.g., '/address/zipcode'.";
public const string SqlContainerThroughputHelpMessage = "The throughput of SQL container (RU/s). Default value is 400.";
Expand Down
59 changes: 59 additions & 0 deletions src/CosmosDB/CosmosDB/Models/PSClientEncryptionIncludedPath.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

using Microsoft.Azure.Management.CosmosDB.Models;
using System.Collections.Generic;

namespace Microsoft.Azure.Commands.CosmosDB.Models
{
public class PSClientEncryptionIncludedPath
{
public PSClientEncryptionIncludedPath()
{
}

public PSClientEncryptionIncludedPath(ClientEncryptionIncludedPath clientEncryptionIncludedPath)
{
if (clientEncryptionIncludedPath == null)
{
return;
}

Path = clientEncryptionIncludedPath.Path;
ClientEncryptionKeyId = clientEncryptionIncludedPath.ClientEncryptionKeyId;
EncryptionType = clientEncryptionIncludedPath.EncryptionType;
EncryptionAlgorithm = clientEncryptionIncludedPath.EncryptionAlgorithm;
}

/// <summary>
/// Gets or sets the path to be encrypted. Must be a top level path, eg. /salary
/// </summary>
public string Path { get; set; }

/// <summary>
/// Gets or sets the identifier of the Client Encryption Key to be used to encrypt the path.
/// </summary>
public string ClientEncryptionKeyId { get; set; }

/// <summary>
/// Gets or sets the type of encryption to be performed. Eg - Deterministic, Randomized
/// </summary>
public string EncryptionType { get; set; }

/// <summary>
/// Gets or sets the encryption algorithm which will be used. Eg - AEAD_AES_256_CBC_HMAC_SHA256
/// </summary>
public string EncryptionAlgorithm { get; set; }
}
}
179 changes: 179 additions & 0 deletions src/CosmosDB/CosmosDB/Models/PSClientEncryptionPolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------
using Microsoft.Azure.Management.CosmosDB.Models;
using Microsoft.Azure.PowerShell.Cmdlets.CosmosDB.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.Azure.Commands.CosmosDB.Models
{

public class PSClientEncryptionPolicy
{
public PSClientEncryptionPolicy()
{
}

public PSClientEncryptionPolicy(ClientEncryptionPolicy clientEncryptionPolicy)
{
if (clientEncryptionPolicy == null)
{
return;
}

if (ModelHelper.IsNotNullOrEmpty(clientEncryptionPolicy.IncludedPaths))
{
PSClientEncryptionPolicy.ValidateIncludedPaths(clientEncryptionPolicy.IncludedPaths);

IncludedPaths = new List<PSClientEncryptionIncludedPath>();
foreach (ClientEncryptionIncludedPath key in clientEncryptionPolicy.IncludedPaths)
{
IncludedPaths.Add(new PSClientEncryptionIncludedPath(key));
}
}

this.PolicyFormatVersion = (int)clientEncryptionPolicy.PolicyFormatVersion;
}

/// <summary>
/// Gets or sets paths of the item that need encryption along with path-specific settings.
/// </summary>
public IList<PSClientEncryptionIncludedPath> IncludedPaths { get; private set; }

/// <summary>
/// Version of the client encryption policy definition.
/// </summary>
public int PolicyFormatVersion { get; private set; }

public static ClientEncryptionPolicy ToSDKModel(PSClientEncryptionPolicy pSClientEncryptionPolicy, List<string> partitionKeyPathTokens)
{
if (pSClientEncryptionPolicy == null)
{
return null;
}

ClientEncryptionPolicy clientEncryptionPolicy = new ClientEncryptionPolicy
{
IncludedPaths = new List<ClientEncryptionIncludedPath>(),
PolicyFormatVersion = pSClientEncryptionPolicy.PolicyFormatVersion
};

if (ModelHelper.IsNotNullOrEmpty(pSClientEncryptionPolicy.IncludedPaths))
{
foreach (PSClientEncryptionIncludedPath includedPath in pSClientEncryptionPolicy.IncludedPaths)
{
ClientEncryptionIncludedPath clientEncryptionIncludedPath = new ClientEncryptionIncludedPath
{
Path = includedPath.Path,
ClientEncryptionKeyId = includedPath.ClientEncryptionKeyId,
EncryptionAlgorithm = includedPath.EncryptionAlgorithm,
EncryptionType = includedPath.EncryptionType
};

clientEncryptionPolicy.IncludedPaths.Add(clientEncryptionIncludedPath);
}
}

PSClientEncryptionPolicy.ValidatePartitionKeyPathsAreNotEncrypted(clientEncryptionPolicy.IncludedPaths, partitionKeyPathTokens);

if(clientEncryptionPolicy.PolicyFormatVersion != 1)
{
throw new InvalidOperationException($"Invalid PolicyFormatVersion:{clientEncryptionPolicy.PolicyFormatVersion} used in Client Encryption Policy. ");
}

return clientEncryptionPolicy;
}

/// <summary>
/// Ensures that partition key paths are not specified in the client encryption policy for encryption.
/// </summary>
/// <param name="partitionKeyPathTokens">Tokens corresponding to validated partition key.</param>
private static void ValidatePartitionKeyPathsAreNotEncrypted(IEnumerable<ClientEncryptionIncludedPath> clientEncryptionIncludedPath, List<string> partitionKeyPathTokens)
{
Debug.Assert(partitionKeyPathTokens != null);
IEnumerable<string> propertiesToEncrypt = clientEncryptionIncludedPath.Select(p => p.Path.Substring(1));
foreach (string tokenInPath in partitionKeyPathTokens)
{
Debug.Assert(String.IsNullOrEmpty(tokenInPath));
if (propertiesToEncrypt.Contains(tokenInPath.Substring(1)))
{
throw new ArgumentException($"Paths which are part of the partition key({tokenInPath}) may not be included in the Client Encryption Policy. ");
}
}
}

private static void ValidateIncludedPaths(IEnumerable<ClientEncryptionIncludedPath> clientEncryptionIncludedPath)
{
List<string> includedPathsList = new List<string>();
foreach (ClientEncryptionIncludedPath path in clientEncryptionIncludedPath)
{
PSClientEncryptionPolicy.ValidateClientEncryptionIncludedPath(path);
if (includedPathsList.Contains(path.Path))
{
throw new ArgumentException($"Duplicate Path({path.Path}) found.", nameof(clientEncryptionIncludedPath));
}

includedPathsList.Add(path.Path);
}
}

private static void ValidateClientEncryptionIncludedPath(ClientEncryptionIncludedPath clientEncryptionIncludedPath)
{
if (clientEncryptionIncludedPath == null)
{
throw new ArgumentNullException(nameof(clientEncryptionIncludedPath));
}

if (string.IsNullOrWhiteSpace(clientEncryptionIncludedPath.Path))
{
throw new ArgumentNullException(nameof(clientEncryptionIncludedPath.Path));
}

if (clientEncryptionIncludedPath.Path[0] != '/'
|| clientEncryptionIncludedPath.Path.LastIndexOf('/') != 0
|| string.Equals(clientEncryptionIncludedPath.Path.Substring(1), "id"))
{
throw new ArgumentException($"Invalid path '{clientEncryptionIncludedPath.Path ?? string.Empty}'.");
}

if (string.IsNullOrWhiteSpace(clientEncryptionIncludedPath.ClientEncryptionKeyId))
{
throw new ArgumentNullException(nameof(clientEncryptionIncludedPath.ClientEncryptionKeyId));
}

if (string.IsNullOrWhiteSpace(clientEncryptionIncludedPath.EncryptionType))
{
throw new ArgumentNullException(nameof(clientEncryptionIncludedPath.EncryptionType));
}

if (!string.Equals(clientEncryptionIncludedPath.EncryptionType, "Deterministic") &&
!string.Equals(clientEncryptionIncludedPath.EncryptionType, "Randomized"))
{
throw new ArgumentException("EncryptionType should be either 'Deterministic' or 'Randomized'. ", nameof(clientEncryptionIncludedPath));
}

if (string.IsNullOrWhiteSpace(clientEncryptionIncludedPath.EncryptionAlgorithm))
{
throw new ArgumentNullException(nameof(clientEncryptionIncludedPath.EncryptionAlgorithm));
}

if (!string.Equals(clientEncryptionIncludedPath.EncryptionAlgorithm, "AEAD_AES_256_CBC_HMAC_SHA256"))
{
throw new ArgumentException("EncryptionAlgorithm should be 'AEAD_AES_256_CBC_HMAC_SHA256'. ", nameof(clientEncryptionIncludedPath));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

using Microsoft.Azure.Management.CosmosDB.Models;

namespace Microsoft.Azure.Commands.CosmosDB.Models
{
public class PSSqlClientEncryptionIncludedPath : PSClientEncryptionIncludedPath
{
public PSSqlClientEncryptionIncludedPath() : base()
{
}

public PSSqlClientEncryptionIncludedPath(ClientEncryptionIncludedPath clientEncryptionIncludedPath) : base(clientEncryptionIncludedPath)
{
}
}
}
Loading