ClientEncryptionPolicy.java
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.cosmos.models;
import com.azure.cosmos.implementation.Constants;
import com.azure.cosmos.implementation.JsonSerializable;
import com.azure.cosmos.implementation.apachecommons.lang.StringUtils;
import com.azure.cosmos.util.Beta;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.azure.cosmos.implementation.guava25.base.Preconditions.checkNotNull;
/**
* Client encryption policy.
*/
@Beta(value = Beta.SinceVersion.V4_14_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING)
public final class ClientEncryptionPolicy {
private JsonSerializable jsonSerializable;
/**
* Paths of the item that need encryption along with path-specific settings.
*/
@JsonProperty("includedPaths")
private List<ClientEncryptionIncludedPath> includedPaths;
@JsonProperty("policyFormatVersion")
private int policyFormatVersion;
/**
* Constructor.
*
* @param paths list of path of the item that need encryption along with path-specific settings.
*/
@Beta(value = Beta.SinceVersion.V4_14_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING)
public ClientEncryptionPolicy(List<ClientEncryptionIncludedPath> paths) {
this.validateIncludedPaths(paths);
this.includedPaths = paths;
this.policyFormatVersion = 1;
}
/**
* Constructor.
*/
public ClientEncryptionPolicy() {
this.jsonSerializable = new JsonSerializable();
}
/**
* Constructor.
*
* @param jsonString the json string that represents the client encryption policy.
*/
ClientEncryptionPolicy(String jsonString) {
this.jsonSerializable = new JsonSerializable(jsonString);
}
/**
* Constructor.
*
* @param objectNode the object node that represents the client encryption policy.
*/
ClientEncryptionPolicy(ObjectNode objectNode) {
this.jsonSerializable = new JsonSerializable(objectNode);
}
/**
* Gets the list of path of the item that need encryption along with path-specific settings.
* @return includedPaths
*/
@Beta(value = Beta.SinceVersion.V4_11_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING)
public List<ClientEncryptionIncludedPath> getIncludedPaths() {
return this.includedPaths;
}
/**
* Version of the client encryption policy definition.
* @return policyFormatVersion
*/
@Beta(value = Beta.SinceVersion.V4_16_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING)
public int getPolicyFormatVersion() {
return policyFormatVersion;
}
void validatePartitionKeyPathsAreNotEncrypted(List<List<String>> partitionKeyPathTokens) {
checkNotNull(partitionKeyPathTokens, "partitionKeyPathTokens cannot be null");
List<String> propertiesToEncrypt =
this.includedPaths.stream().map(clientEncryptionIncludedPath -> clientEncryptionIncludedPath.getPath().substring(1)).collect(Collectors.toList());
for (List<String> tokensInPath : partitionKeyPathTokens) {
checkNotNull(tokensInPath);
if (tokensInPath.size() > 0) {
String topLevelToken = tokensInPath.get(0);
if (propertiesToEncrypt.contains(topLevelToken)) {
throw new IllegalArgumentException(String.format("Path %s which is part of the partition key " +
"cannot be included" +
" in the ClientEncryptionPolicy.", topLevelToken));
}
}
}
}
private void validateIncludedPaths(List<ClientEncryptionIncludedPath> clientEncryptionIncludedPath) {
List<String> includedPathsList = new ArrayList<>();
for (ClientEncryptionIncludedPath path : clientEncryptionIncludedPath) {
this.validateClientEncryptionIncludedPath(path);
if (includedPathsList.contains(path.getPath())) {
throw new IllegalArgumentException("Duplicate Path found in clientEncryptionIncludedPath.");
}
includedPathsList.add(path.getPath());
}
}
private void validateClientEncryptionIncludedPath(ClientEncryptionIncludedPath clientEncryptionIncludedPath) {
if (clientEncryptionIncludedPath == null) {
throw new IllegalArgumentException("clientEncryptionIncludedPath is null");
}
if (StringUtils.isEmpty(clientEncryptionIncludedPath.getPath())) {
throw new IllegalArgumentException("path in clientEncryptionIncludedPath is empty");
}
if (clientEncryptionIncludedPath.getPath().charAt(0) != '/'
|| clientEncryptionIncludedPath.getPath().lastIndexOf('/') != 0
|| clientEncryptionIncludedPath.getPath().substring(1).equals("id")) {
throw new IllegalArgumentException("Invalid path " + clientEncryptionIncludedPath.getPath());
}
if (StringUtils.isEmpty(clientEncryptionIncludedPath.getClientEncryptionKeyId())) {
throw new IllegalArgumentException("clientEncryptionKeyId in clientEncryptionIncludedPath is empty");
}
if (StringUtils.isEmpty(clientEncryptionIncludedPath.getEncryptionType())) {
throw new IllegalArgumentException("encryptionType in clientEncryptionIncludedPath is empty");
}
if (!clientEncryptionIncludedPath.getEncryptionType().equals(Constants.Properties.DETERMINISTIC) &&
!clientEncryptionIncludedPath.getEncryptionType().equals(Constants.Properties.RANDOMIZED)) {
throw new IllegalArgumentException("EncryptionType should be either 'Deterministic' or 'Randomized'.");
}
if (StringUtils.isEmpty(clientEncryptionIncludedPath.getEncryptionAlgorithm())) {
throw new IllegalArgumentException("encryptionAlgorithm in clientEncryptionIncludedPath is empty");
}
if (!clientEncryptionIncludedPath.getEncryptionAlgorithm().equals("AEAD_AES_256_CBC_HMAC_SHA256")) {
throw new IllegalArgumentException("EncryptionAlgorithm should be 'AEAD_AES_256_CBC_HMAC_SHA256'.");
}
}
}