Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
eu.clarussecure.proxy.protocol.ProtocolServiceDelegate Maven / Gradle / Ivy
package eu.clarussecure.proxy.protocol;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import eu.clarussecure.dataoperations.Criteria;
import eu.clarussecure.dataoperations.DataOperationCommand;
import eu.clarussecure.dataoperations.DataOperationResponse;
import eu.clarussecure.dataoperations.DataOperationResult;
import eu.clarussecure.proxy.spi.CString;
import eu.clarussecure.proxy.spi.DataOperation;
import eu.clarussecure.proxy.spi.InboundDataOperation;
import eu.clarussecure.proxy.spi.MetadataOperation;
import eu.clarussecure.proxy.spi.OutboundDataOperation;
import eu.clarussecure.proxy.spi.OutboundDataOperation.Criterion;
import eu.clarussecure.proxy.spi.StringUtilities;
import eu.clarussecure.proxy.spi.protection.ProtectionModule;
import eu.clarussecure.proxy.spi.protocol.ProtocolService;
public class ProtocolServiceDelegate implements ProtocolService {
private ProtectionModule protectionModule;
public ProtocolServiceDelegate(ProtectionModule protectionModule) {
this.protectionModule = protectionModule;
}
@Override
public CString[] userAuthentication(CString user, CString password) {
return new CString[] { user, password };
}
@Override
public CString newUserIdentification(CString user) {
return user;
}
@Override
public MetadataOperation newMetadataOperation(MetadataOperation metadataOperation) {
Objects.requireNonNull(metadataOperation);
String[] attributeNames = metadataOperation.getDataIds().stream().map(CString::toString).toArray(String[]::new);
List> mapping = head(attributeNames);
List moduleFQAttributeNames = mapping.stream().map(Map::keySet).flatMap(Set::stream).distinct()
.collect(Collectors.toList());
String[] fqAttributeNames = Arrays.stream(attributeNames).flatMap(attributeName -> {
Stream stream = Stream.of(attributeName);
if (attributeName.indexOf('*') != -1) {
Pattern pattern = Pattern.compile(escapeRegex(attributeName));
if (moduleFQAttributeNames.stream().anyMatch(fqan -> pattern.matcher(fqan).matches())) {
stream = moduleFQAttributeNames.stream().filter(fqan -> pattern.matcher(fqan).matches());
}
}
return stream;
}).toArray(String[]::new);
// add the additional attributes that the protection module has added
Stream additionalAttributeNames = moduleFQAttributeNames.stream()
.filter(mfqan -> !Arrays.asList(fqAttributeNames).contains(mfqan));
metadataOperation.getMetadata().clear();
Map> metadata = Stream.concat(Arrays.stream(fqAttributeNames), additionalAttributeNames)
.distinct()
.collect(Collectors.toMap(CString::valueOf,
an -> mapping.stream().anyMatch(map -> map.containsKey(an)) ? mapping.stream()
.map(map -> map.get(an)).map(CString::valueOf).collect(Collectors.toList())
: Collections.emptyList()));
metadataOperation.setMetadata(metadata.entrySet().stream().collect(Collectors.toList()));
metadataOperation.setInvolvedCSPs(
IntStream.range(0, mapping.size()).mapToObj(Integer::valueOf).collect(Collectors.toList()));
metadataOperation.setModified(true);
return metadataOperation;
}
@Override
public List newOutboundDataOperation(OutboundDataOperation outboundDataOperation) {
Objects.requireNonNull(outboundDataOperation);
List newOutboundDataOperations;
if (outboundDataOperation.getOperation() != null) {
switch (outboundDataOperation.getOperation()) {
case CREATE:
newOutboundDataOperations = newCreateOperation(outboundDataOperation);
break;
case READ:
newOutboundDataOperations = newOutboundReadOperation(outboundDataOperation);
break;
case UPDATE:
newOutboundDataOperations = newUpdateOperation(outboundDataOperation);
break;
case DELETE:
newOutboundDataOperations = newDeleteOperation(outboundDataOperation);
break;
default:
newOutboundDataOperations = Collections.singletonList(outboundDataOperation);
break;
}
} else {
newOutboundDataOperations = broadcastOutboundOperation(outboundDataOperation);
}
return newOutboundDataOperations;
}
@Override
public InboundDataOperation newInboundDataOperation(List inboundDataOperations) {
Objects.requireNonNull(inboundDataOperations);
InboundDataOperation newInboundDataOperation = null;
if (inboundDataOperations.stream().map(InboundDataOperation::getOperation).distinct().count() == 1) {
if (inboundDataOperations.get(0).getOperation() != null) {
switch (inboundDataOperations.get(0).getOperation()) {
case READ:
newInboundDataOperation = newInboundReadOperation(inboundDataOperations);
break;
default:
newInboundDataOperation = inboundDataOperations.get(0);
break;
}
} else {
newInboundDataOperation = inboundDataOperations.get(0);
}
}
return newInboundDataOperation;
}
private List newCreateOperation(OutboundDataOperation outboundDataOperation) {
List newOutboundDataOperations = null;
// Filter attribute names according to the mapping
// (in order to pass only attributes that have to be protected)
String[] allAttributeNames = outboundDataOperation.getDataIds().stream().map(CString::toString)
.toArray(String[]::new);
List> mappings = head(allAttributeNames);
int numberOfCSPs = mappings.size();
List protectedAttributes = Arrays.stream(allAttributeNames)
.map(an -> mappings.stream().anyMatch(map -> map.containsKey(an))).collect(Collectors.toList());
String[] attributeNames = IntStream.range(0, allAttributeNames.length).filter(i -> protectedAttributes.get(i))
.mapToObj(i -> allAttributeNames[i]).toArray(String[]::new);
if (attributeNames.length > 0) {
// at least one attribute has to be protected
String[][] allContents;
String[][] contents;
String[][] newAttributeNames;
String[][][] newContents;
List> mappingIndexes;
String[][] newExtraProtectedAttributeNames;
InputStream[][] newExtraBinaryContents;
allContents = outboundDataOperation.getDataValues().stream()
.map(row -> row.stream().map(StringUtilities::toString).toArray(String[]::new))
.toArray(String[][]::new);
contents = Arrays.stream(allContents)
.map(allRow -> IntStream.range(0, protectedAttributes.size())
.filter(i -> protectedAttributes.get(i)).mapToObj(i -> allRow[i]).toArray(String[]::new))
.toArray(String[][]::new);
if (outboundDataOperation.isUsingHeadOperation()) {
// using head operation is more efficient when there is no
// content to protect or content is not to protect
newAttributeNames = mappings
.stream().map(
map -> Stream
.concat(Arrays.stream(attributeNames).filter(an -> map.containsKey(an))
.map(an -> map.get(an)),
map.entrySet().stream()
.filter(e -> !Arrays.asList(attributeNames)
.contains(e.getKey()))
.map(Map.Entry::getValue))
.toArray(String[]::new))
.toArray(String[][]::new);
newContents = mappings.stream()
.map(map -> Arrays.stream(contents)
.map(row -> IntStream.range(0, attributeNames.length)
.filter(c -> map.containsKey(attributeNames[c])).mapToObj(c -> row[c])
.toArray(String[]::new))
.toArray(String[][]::new))
.toArray(String[][][]::new);
mappingIndexes = IntStream
.range(0,
mappings.size())
.mapToObj(
csp -> mappings.get(csp).entrySet().stream()
.collect(
Collectors.toMap(Map.Entry::getKey,
e -> IntStream.range(0, newAttributeNames[csp].length)
.filter(c -> newAttributeNames[csp][c]
.equals(e.getValue()))
.findFirst().getAsInt())))
.collect(Collectors.toList());
newExtraProtectedAttributeNames = new String[numberOfCSPs][0];
newExtraBinaryContents = new InputStream[numberOfCSPs][0];
} else {
// using post operation allow to protect content
List results = post(attributeNames, contents);
if (results == null) {
newAttributeNames = Stream.generate(() -> attributeNames).limit(numberOfCSPs)
.toArray(String[][]::new);
newContents = new String[numberOfCSPs][0][];
mappingIndexes = Stream.>generate(() -> Collections.emptyMap())
.limit(numberOfCSPs).collect(Collectors.toList());
newExtraProtectedAttributeNames = new String[numberOfCSPs][0];
newExtraBinaryContents = new InputStream[numberOfCSPs][0];
} else {
newAttributeNames = results.stream().map(DataOperationCommand::getProtectedAttributeNames)
.map(pans -> pans == null ? new String[0] : pans).toArray(String[][]::new);
newContents = results.stream().map(DataOperationCommand::getProtectedContents)
.toArray(String[][][]::new);
mappingIndexes = results.stream()
.map(result -> result.getMapping().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
e -> IntStream.range(0, result.getProtectedAttributeNames().length)
.filter(i -> result.getProtectedAttributeNames()[i]
.equals(e.getValue()))
.findFirst().getAsInt())))
.collect(Collectors.toList());
newExtraProtectedAttributeNames = results.stream()
.map(DataOperationCommand::getExtraProtectedAttributeNames)
.map(epans -> epans == null ? new String[0] : epans).toArray(String[][]::new);
newExtraBinaryContents = results.stream().map(DataOperationCommand::getExtraBinaryContent)
.map(ebcs -> ebcs == null ? new InputStream[0] : ebcs).toArray(InputStream[][]::new);
}
}
if (numberOfCSPs > 1 // more than one csp
// one csp but attribute names change
|| (numberOfCSPs == 1
&& !mappings.get(0).entrySet().stream().allMatch(e -> e.getKey().equals(e.getValue())))
// one csp but content change
|| (newContents.length == 1 && !Arrays.equals(contents, newContents[0]))) {
newOutboundDataOperations = new ArrayList<>(numberOfCSPs);
for (int csp = 0; csp < numberOfCSPs; csp++) {
if (outboundDataOperation.getInvolvedCSPs() != null
&& !outboundDataOperation.getInvolvedCSPs().contains(csp)) {
continue;
}
Map cspMapping = mappings.get(csp);
if (cspMapping.size() == 0) {
continue;
}
Map cspMappingIndexes = mappingIndexes.get(csp);
OutboundDataOperation newOutboundDataOperation = new OutboundDataOperation();
newOutboundDataOperation.setAttributes(outboundDataOperation.getAttributes());
newOutboundDataOperation.setRequestId(outboundDataOperation.getRequestId());
newOutboundDataOperation.setOperation(outboundDataOperation.getOperation());
newOutboundDataOperation.setUsingHeadOperation(outboundDataOperation.isUsingHeadOperation());
newOutboundDataOperation
.setUnprotectingDataEnabled(outboundDataOperation.isUnprotectingDataEnabled());
List dataIds = new ArrayList<>(protectedAttributes.size());
String[] cspNewAttributeNames = newAttributeNames[csp];
for (int c = 0; c < protectedAttributes.size(); c++) {
if (protectedAttributes.get(c)) {
String newAttributeName = cspMapping.get(allAttributeNames[c]);
if (newAttributeName != null && Arrays.stream(cspNewAttributeNames)
.anyMatch(nan -> nan.equals(newAttributeName))) {
dataIds.add(CString.valueOf(newAttributeName));
}
} else {
dataIds.add(CString.valueOf(allAttributeNames[c]));
}
}
// add additional attributes
List newAttributeNamesIndex = Arrays.stream(cspNewAttributeNames).map(CString::valueOf)
.map(nan -> dataIds.indexOf(nan)).collect(Collectors.toList());
for (int c = 0; c < newAttributeNamesIndex.size(); c++) {
if (newAttributeNamesIndex.get(c) == -1) {
dataIds.add(CString.valueOf(cspNewAttributeNames[c]));
}
}
newOutboundDataOperation.setDataIds(dataIds);
String[][] cspNewContents = newContents[csp];
List> newDataValues = new ArrayList<>(allContents.length);
for (int r = 0; r < allContents.length; r++) {
List rowDataValues = new ArrayList<>(protectedAttributes.size());
for (int c = 0; c < protectedAttributes.size(); c++) {
if (protectedAttributes.get(c)) {
String newAttributeName = cspMapping.get(allAttributeNames[c]);
if (newAttributeName != null && Arrays.stream(cspNewAttributeNames)
.anyMatch(nan -> nan.equals(newAttributeName))) {
rowDataValues.add(CString
.valueOf(cspNewContents[r][cspMappingIndexes.get(allAttributeNames[c])]));
}
} else {
rowDataValues.add(CString.valueOf(allContents[r][c]));
}
}
// add additional data values
for (int c = 0; c < newAttributeNamesIndex.size(); c++) {
if (newAttributeNamesIndex.get(c) == -1) {
rowDataValues.add(CString.valueOf(cspNewContents[r][c]));
}
}
newDataValues.add(rowDataValues);
}
newOutboundDataOperation.setDataValues(newDataValues);
Map dataIdMapping = cspMapping.entrySet().stream().collect(
Collectors.toMap(e -> CString.valueOf(e.getKey()), e -> CString.valueOf(e.getValue())));
newOutboundDataOperation.setDataIdMapping(dataIdMapping);
// Add extra protected attribute names
String[] cspNewExtraProtectedAttributeNames = newExtraProtectedAttributeNames[csp];
List newExtraDataIds = Arrays.stream(cspNewExtraProtectedAttributeNames)
.map(CString::valueOf).collect(Collectors.toList());
newOutboundDataOperation.setExtraDataIds(newExtraDataIds);
InputStream[] cspNewExtraBinaryContents = newExtraBinaryContents[csp];
List newExtraDataContents = Arrays.stream(cspNewExtraBinaryContents)
.collect(Collectors.toList());
newOutboundDataOperation.setExtraDataContents(newExtraDataContents);
newOutboundDataOperation.setModified(true);
newOutboundDataOperation.setInvolvedCSP(csp);
newOutboundDataOperations.add(newOutboundDataOperation);
}
}
}
if (newOutboundDataOperations == null) {
// nothing to protect
if (outboundDataOperation.getInvolvedCSPs() != null) {
// retain only the first CSP
outboundDataOperation.getInvolvedCSPs().removeIf(csp -> csp != 0);
}
if (outboundDataOperation.getInvolvedCSPs() == null || !outboundDataOperation.getInvolvedCSPs().isEmpty()) {
Map dataIdMapping = outboundDataOperation.getDataIds().stream().distinct()
.collect(Collectors.toMap(Function.identity(), Function.identity()));
outboundDataOperation.setDataIdMapping(dataIdMapping);
outboundDataOperation.setInvolvedCSP(0);
newOutboundDataOperations = Collections.singletonList(outboundDataOperation);
} else {
newOutboundDataOperations = Collections.emptyList();
}
}
return newOutboundDataOperations;
}
private List newOutboundReadOperation(OutboundDataOperation outboundDataOperation) {
if (outboundDataOperation.isUsingHeadOperation()) {
// using head operation is not supported (because of the
// promise)
throw new IllegalArgumentException("Read operation cannot rely only on the head operation");
}
List newOutboundDataOperations = null;
// Filter attribute names according to the mapping
// (in order to pass only attributes that have to be protected)
String[] allAttributeNames = outboundDataOperation.getDataIds().stream().map(CString::toString)
.toArray(String[]::new);
List allCriterions = outboundDataOperation.getCriterions();
String[] allCriteriaAttributeNames = allCriterions.stream()
.map(criterion -> criterion != null ? criterion.getDataId().toString() : null).toArray(String[]::new);
List> mappings = head(Stream
.concat(Stream.of(allAttributeNames), Stream.of(allCriteriaAttributeNames).filter(can -> can != null))
.toArray(String[]::new));
int numberOfCSPs = mappings.size();
List fqAttributeNames = mappings.stream().map(Map::keySet).flatMap(Set::stream).distinct()
.filter(fqan -> {
String an = fqan.substring(fqan.lastIndexOf('/') + 1);
return an.charAt(0) != '?' || an.charAt(an.length() - 1) != '?';
}).collect(Collectors.toList());
String[] allFQAttributeNames = Arrays.stream(allAttributeNames).flatMap(attributeName -> {
Stream stream = Stream.of(attributeName);
if (attributeName.indexOf('*') != -1) {
Pattern pattern = Pattern.compile(escapeRegex(attributeName));
if (fqAttributeNames.stream().anyMatch(fqan -> pattern.matcher(fqan).matches())) {
stream = fqAttributeNames.stream().filter(fqan -> pattern.matcher(fqan).matches());
}
}
return stream;
}).toArray(String[]::new);
List protectedAttributes = Arrays.stream(allFQAttributeNames)
.map(an -> mappings.stream().anyMatch(map -> map.containsKey(an))).collect(Collectors.toList());
String[] attributeNames = IntStream.range(0, allFQAttributeNames.length).filter(i -> protectedAttributes.get(i))
.mapToObj(i -> allFQAttributeNames[i]).distinct().toArray(String[]::new);
CString[][] allDataValues = outboundDataOperation.getDataValues().stream()
.map(row -> row.stream().toArray(CString[]::new)).toArray(CString[][]::new);
CString[][] allFQDataValues = Arrays.stream(allDataValues)
.map(row -> IntStream.range(0, allAttributeNames.length).flatMap(c -> {
IntStream stream = IntStream.of(c);
if (allAttributeNames[c].indexOf('*') != -1) {
Pattern pattern = Pattern.compile(escapeRegex(allAttributeNames[c]));
if (fqAttributeNames.stream().anyMatch(fqan -> pattern.matcher(fqan).matches())) {
stream = IntStream.generate(() -> c).limit(
fqAttributeNames.stream().filter(fqan -> pattern.matcher(fqan).matches()).count());
}
}
return stream;
}).mapToObj(c -> row[c]).toArray(CString[]::new)).toArray(CString[][]::new);
List dataValuesToProcess = IntStream.range(0, allFQAttributeNames.length)
.mapToObj(c -> protectedAttributes.get(c)
&& Arrays.asList(allFQAttributeNames).indexOf(allFQAttributeNames[c]) == c)
.collect(Collectors.toList());
Criteria[] allCriteria = allCriterions.stream()
.map(criterion -> criterion != null ? new Criteria(criterion.getDataId().toString(),
criterion.getOperator().toString(), criterion.getValue().toString()) : null)
.toArray(Criteria[]::new);
List protectedCriteria = Arrays.stream(allCriteriaAttributeNames)
.map(can -> can != null && mappings.stream().anyMatch(map -> map.containsKey(can)))
.collect(Collectors.toList());
Criteria[] criteria = IntStream.range(0, allCriteria.length).filter(i -> protectedCriteria.get(i))
.mapToObj(i -> allCriteria[i]).toArray(Criteria[]::new);
if (attributeNames.length > 0 || criteria.length > 0) {
// at least one attribute or criteria has to be protected
List promise = get(attributeNames, criteria);
String[][] newAttributeNames = promise == null
? Stream.generate(() -> attributeNames).limit(numberOfCSPs).toArray(String[][]::new)
: promise.stream().map(DataOperationCommand::getProtectedAttributeNames)
.map(pans -> pans == null ? new String[0] : pans).toArray(String[][]::new);
Criteria[][] newCriteria = promise == null
? Stream.generate(() -> criteria).limit(numberOfCSPs).toArray(Criteria[][]::new)
: promise.stream().map(DataOperationCommand::getCriteria)
.map(cs -> cs == null ? new Criteria[0] : cs).toArray(Criteria[][]::new);
if (numberOfCSPs > 1 // more than one csp
// one csp but attribute names change
|| (numberOfCSPs == 1 && !mappings.get(0).entrySet().stream()
.allMatch(e -> e.getValue() == null || e.getKey().equals(e.getValue())))
// one csp but criteria change
|| (newCriteria.length == 1 && !Arrays.equals(criteria, newCriteria[0]))) {
newOutboundDataOperations = new ArrayList<>(numberOfCSPs);
for (int csp = 0; csp < numberOfCSPs; csp++) {
if (outboundDataOperation.getInvolvedCSPs() != null
&& !outboundDataOperation.getInvolvedCSPs().contains(csp)) {
continue;
}
Map cspMapping = mappings.get(csp);
if (cspMapping.size() == 0 || newAttributeNames[csp].length == 0) {
continue;
}
OutboundDataOperation newOutboundDataOperation = new OutboundDataOperation();
newOutboundDataOperation.setAttributes(outboundDataOperation.getAttributes());
newOutboundDataOperation.setRequestId(outboundDataOperation.getRequestId());
newOutboundDataOperation.setOperation(outboundDataOperation.getOperation());
newOutboundDataOperation.setUsingHeadOperation(outboundDataOperation.isUsingHeadOperation());
newOutboundDataOperation
.setUnprotectingDataEnabled(outboundDataOperation.isUnprotectingDataEnabled());
List dataIds = new ArrayList<>(protectedAttributes.size());
String[] cspNewAttributeNames = newAttributeNames[csp];
for (int c = 0; c < protectedAttributes.size(); c++) {
if (protectedAttributes.get(c)) {
String newAttributeName = cspMapping.get(allFQAttributeNames[c]);
if (newAttributeName != null && Arrays.stream(cspNewAttributeNames)
.anyMatch(nan -> nan.equals(newAttributeName))) {
dataIds.add(CString.valueOf(newAttributeName));
}
} else {
dataIds.add(CString.valueOf(allFQAttributeNames[c]));
}
}
// add additional attributes
List newAttributeNamesIndex = Arrays.stream(cspNewAttributeNames).map(CString::valueOf)
.map(nan -> dataIds.indexOf(nan)).collect(Collectors.toList());
for (int c = 0; c < newAttributeNamesIndex.size(); c++) {
if (newAttributeNamesIndex.get(c) == -1) {
dataIds.add(CString.valueOf(cspNewAttributeNames[c]));
}
}
newOutboundDataOperation.setDataIds(dataIds);
List> newDataValues = new ArrayList<>(allFQDataValues.length);
for (int r = 0; r < allFQDataValues.length; r++) {
List newRow = new ArrayList<>(dataValuesToProcess.size());
for (int c = 0; c < dataValuesToProcess.size(); c++) {
if (dataValuesToProcess.get(c)) {
String newAttributeName = cspMapping.get(allFQAttributeNames[c]);
if (newAttributeName != null && Arrays.stream(newAttributeNames[csp])
.anyMatch(nan -> nan.equals(newAttributeName))) {
newRow.add(allFQDataValues[r][c]);
}
} else {
newRow.add(allFQDataValues[r][c]);
}
}
// add additional data values
for (int c = 0; c < newAttributeNamesIndex.size(); c++) {
if (newAttributeNamesIndex.get(c) == -1) {
newRow.add(null);
}
}
newDataValues.add(newRow);
}
newOutboundDataOperation.setDataValues(newDataValues);
Map dataIdMapping = cspMapping.entrySet().stream().collect(
Collectors.toMap(e -> CString.valueOf(e.getKey()), e -> CString.valueOf(e.getValue())));
newOutboundDataOperation.setDataIdMapping(dataIdMapping);
List> newCriteriaTrack = Arrays.stream(newCriteria[csp])
.map(nc -> new SimpleEntry<>(nc, false)).collect(Collectors.toList());
List criterions = new ArrayList<>(protectedCriteria.size());
for (int i = 0; i < protectedCriteria.size(); i++) {
if (protectedCriteria.get(i)) {
String clearCriteriaAttributeName = allCriteriaAttributeNames[i];
String cspProtectedCriteriaAttributeName = cspMapping.get(clearCriteriaAttributeName);
Map.Entry entry = null;
if (cspProtectedCriteriaAttributeName != null) {
entry = newCriteriaTrack.stream().filter(e -> !e.getValue()).findFirst().orElse(null);
}
if (entry != null) {
entry.setValue(true);
Criteria cspProtectedCriteria = entry.getKey();
criterions.add(new OutboundDataOperation.Criterion(
CString.valueOf(cspProtectedCriteria.getAttributeName()),
CString.valueOf(cspProtectedCriteria.getOperator()),
CString.valueOf(cspProtectedCriteria.getValue())));
}
} else {
criterions.add(allCriterions.get(i));
}
}
newOutboundDataOperation.setCriterions(criterions);
newOutboundDataOperation.setPromise(promise);
boolean modified = outboundDataOperation.isModified();
if (!modified) {
modified = numberOfCSPs > 1;
}
if (!modified) {
modified = !cspMapping.entrySet().stream()
.allMatch(e -> e.getValue() == null || e.getKey().equals(e.getValue()));
}
if (!modified) {
String[] criteriaOperators = Arrays.stream(criteria).map(Criteria::getOperator)
.toArray(String[]::new);
String[] protectedCriteriaOperators = Arrays.stream(newCriteria[csp]).map(Criteria::getOperator)
.toArray(String[]::new);
modified = !Arrays.equals(criteriaOperators, protectedCriteriaOperators);
}
if (!modified) {
String[] criteriaValues = Arrays.stream(criteria).map(Criteria::getValue)
.toArray(String[]::new);
String[] protectedCriteriaValues = Arrays.stream(newCriteria[csp]).map(Criteria::getValue)
.toArray(String[]::new);
modified = !Arrays.equals(criteriaValues, protectedCriteriaValues);
}
newOutboundDataOperation.setModified(modified);
newOutboundDataOperation.setInvolvedCSP(csp);
newOutboundDataOperations.add(newOutboundDataOperation);
}
}
}
if (newOutboundDataOperations == null) {
// nothing to protect
if (outboundDataOperation.getInvolvedCSPs() != null) {
// retain only the CSPs involved by the protection module
outboundDataOperation.getInvolvedCSPs().removeIf(csp -> csp >= numberOfCSPs);
}
if (outboundDataOperation.getInvolvedCSPs() == null || !outboundDataOperation.getInvolvedCSPs().isEmpty()) {
if (outboundDataOperation.getInvolvedCSPs() == null
|| outboundDataOperation.getInvolvedCSPs().size() == 1 || !outboundDataOperation.isModified()) {
Map dataIdMapping = outboundDataOperation.getDataIds().stream().distinct()
.collect(Collectors.toMap(Function.identity(), Function.identity()));
outboundDataOperation.setDataIdMapping(dataIdMapping);
newOutboundDataOperations = Collections.singletonList(outboundDataOperation);
} else {
// broadcast operation to all involved CSPs
newOutboundDataOperations = outboundDataOperation.getInvolvedCSPs().stream().map(csp -> {
OutboundDataOperation newOutboundDataOperation = new OutboundDataOperation();
newOutboundDataOperation.setAttributes(outboundDataOperation.getAttributes());
newOutboundDataOperation.setRequestId(outboundDataOperation.getRequestId());
newOutboundDataOperation.setOperation(outboundDataOperation.getOperation());
newOutboundDataOperation.setUsingHeadOperation(outboundDataOperation.isUsingHeadOperation());
newOutboundDataOperation
.setUnprotectingDataEnabled(outboundDataOperation.isUnprotectingDataEnabled());
newOutboundDataOperation.setDataIds(new ArrayList<>(outboundDataOperation.getDataIds()));
newOutboundDataOperation.setDataValues(new ArrayList<>(outboundDataOperation.getDataValues()));
Map dataIdMapping = newOutboundDataOperation.getDataIds().stream().distinct()
.collect(Collectors.toMap(Function.identity(), Function.identity()));
newOutboundDataOperation.setDataIdMapping(dataIdMapping);
newOutboundDataOperation
.setCriterions(outboundDataOperation.getCriterions().stream()
.map(criterion -> new OutboundDataOperation.Criterion(criterion.getDataId(),
criterion.getOperator(), criterion.getValue()))
.collect(Collectors.toList()));
newOutboundDataOperation.setPromise(outboundDataOperation.getPromise());
newOutboundDataOperation.setModified(true);
newOutboundDataOperation.setInvolvedCSP(csp);
return newOutboundDataOperation;
}).collect(Collectors.toList());
}
} else {
newOutboundDataOperations = Collections.emptyList();
}
}
return newOutboundDataOperations;
}
private String escapeRegex(String regex) {
return regex.replace(".", "\\.").replace("[", "\\[").replace("]", "\\]").replace("(", "\\(").replace(")", "\\)")
.replace("*", "[^/]*");
}
private InboundDataOperation newInboundReadOperation(List inboundDataOperations) {
inboundDataOperations.forEach(dataOperation -> {
if (dataOperation.isUsingHeadOperation()) {
// using head operation is not supported (because of the
// promise)
throw new IllegalArgumentException("Read operation cannot rely only on the head operation");
}
});
InboundDataOperation newInboundDataOperation = null;
@SuppressWarnings("serial")
DataOperationCommand defaultDataOperationCommand = new DataOperationCommand() {
@Override
public Map getMapping() {
return Collections.emptyMap();
}
};
List promise = inboundDataOperations.stream().map(DataOperation::getPromise)
.filter(p -> p != null).findAny().orElseGet(() -> {
int numberOfCSPs = inboundDataOperations.stream().map(DataOperation::getInvolvedCSP)
.max(Comparator.naturalOrder()).orElse(0);
return Stream.generate(() -> defaultDataOperationCommand).limit(numberOfCSPs)
.collect(Collectors.toList());
});
List> mappings = promise.stream().map(DataOperationCommand::getMapping)
.collect(Collectors.toList());
int numberOfCSPs = mappings.size();
// Filter protected attribute names according to the mapping
// (in order to pass only protected attributes that have to be
// unprotected)
String[] allAttributeNames = inboundDataOperations.stream().map(InboundDataOperation::getClearDataIds).findAny()
.get().stream().map(StringUtilities::toString).toArray(String[]::new);
List allProtectedAttributeNames = IntStream.range(0, numberOfCSPs)
.mapToObj(csp -> inboundDataOperations.stream().filter(ido -> ido.getInvolvedCSP() == csp).findFirst()
.orElse(null))
.map(ido -> ido == null ? new String[0]
: ido.getDataIds().stream().map(StringUtilities::toString).toArray(String[]::new))
.collect(Collectors.toList());
List> allProtectedAttributeProtectionFlags = IntStream.range(0, numberOfCSPs)
.mapToObj(csp -> Arrays.stream(allProtectedAttributeNames.get(csp))
.map(pan -> mappings.get(csp).values().contains(pan)).collect(Collectors.toList()))
.collect(Collectors.toList());
List protectedAttributeNames = IntStream.range(0, numberOfCSPs)
.mapToObj(csp -> IntStream.range(0, allProtectedAttributeProtectionFlags.get(csp).size())
.filter(i -> allProtectedAttributeProtectionFlags.get(csp).get(i))
.mapToObj(i -> allProtectedAttributeNames.get(csp)[i]).distinct().toArray(String[]::new))
.collect(Collectors.toList());
if (protectedAttributeNames.stream().map(Array::getLength).anyMatch(len -> len > 0)) {
// at least one attribute to unprotect
// all the protected content (including content that don't need to
// be unprotected and content for duplicated attribute names) for
// all CSPs
List allProtectedContents = IntStream.range(0, numberOfCSPs)
.mapToObj(csp -> inboundDataOperations.stream().filter(ido -> ido.getInvolvedCSP() == csp)
.findFirst().orElse(null))
.map(ido -> ido == null ? new String[0][]
: ido.getDataValues().stream()
.map(row -> row.stream().map(StringUtilities::toString).toArray(String[]::new))
.toArray(String[][]::new))
.collect(Collectors.toList());
// indexes in the protected attribute names expected by the
// protection module for all the request protected attribute names
// (-1 for protected attribute names that don't need to be
// unprotected and for duplicated protected attribute names) and for
// all CSPs
List>> allProtectedAttributeNameIndexes = IntStream.range(0, numberOfCSPs)
.mapToObj(csp -> {
String[] pans = allProtectedAttributeNames.get(csp);
String[] cspProtectedAttributeNames = promise.get(csp).getProtectedAttributeNames().clone();
return Arrays.stream(pans).>map(pan -> {
int index = IntStream.range(0, cspProtectedAttributeNames.length)
.filter(i -> pan.equals(cspProtectedAttributeNames[i])).findFirst().orElse(-1);
if (index != -1) {
// avoid duplicated protected attribute names
cspProtectedAttributeNames[index] = null;
}
return new SimpleEntry<>(pan, index);
}).collect(Collectors.toList());
}).collect(Collectors.toList());
// protected content to pass to the protection module (excluding
// content that don't need to be unprotected and excluding content
// for duplicated attribute names and sorted according to the order
// of the protected attributes defined in the promise) for all CSPs
List moduleProtectedContents = IntStream.range(0, numberOfCSPs)
.mapToObj(
csp -> Arrays.stream(allProtectedContents.get(csp))
.map(allRow -> IntStream.range(0, allProtectedAttributeNameIndexes.get(csp).size())
.filter(i -> allProtectedAttributeNameIndexes.get(csp).get(i)
.getValue() != -1)
.mapToObj(Integer::valueOf)
.sorted((i1, i2) -> allProtectedAttributeNameIndexes.get(csp).get(i1)
.getValue()
- allProtectedAttributeNameIndexes.get(csp).get(i2).getValue())
.map(i -> allRow[i]).toArray(String[]::new))
.toArray(String[][]::new))
.collect(Collectors.toList());
List results = get(promise, moduleProtectedContents);
String[] moduleNewAttributeNames;
String[][] moduleNewContents;
if (results == null || (results.size() == 1 && results.get(0) instanceof DataOperationResponse)) {
moduleNewAttributeNames = results == null ? new String[0]
: ((DataOperationResponse) results.get(0)).getAttributeNames();
moduleNewContents = results == null ? new String[0][]
: ((DataOperationResponse) results.get(0)).getContents();
} else {
throw new IllegalStateException("not yet supported");
}
boolean same = true;
if (numberOfCSPs != 1) {
same = false;
} else {
String[][] cspModuleProtectedContents = moduleProtectedContents.get(0);
if (cspModuleProtectedContents != moduleNewContents) {
if (cspModuleProtectedContents.length != moduleNewContents.length) {
same = false;
} else {
loop1: for (int i = 0; i < cspModuleProtectedContents.length; i++) {
if (cspModuleProtectedContents[i] != moduleNewContents[i]) {
if (cspModuleProtectedContents[i].length != moduleNewContents[i].length) {
same = false;
break loop1;
} else {
for (int j = 0; j < cspModuleProtectedContents[i].length; j++) {
if (cspModuleProtectedContents[i][j] != moduleNewContents[i][j]) {
same = false;
break loop1;
}
}
}
}
}
}
}
}
if (!same) {
String[][] newContents = moduleNewContents;
// indexes in the attribute names for each request attribute
// name (-1 if an attribute name didn't need to be unprotected)
List> allAttributeNameIndexes = Arrays
.stream(allAttributeNames).>map(an -> {
int index = IntStream.range(0, moduleNewAttributeNames.length)
.filter(i -> an.equals(moduleNewAttributeNames[i])).findFirst().orElse(-1);
return new SimpleEntry<>(an, index);
}).collect(Collectors.toList());
// prepare flags to know if content must be duplicated
boolean[][] newContentFlags = Arrays.stream(newContents).map(row -> new boolean[row.length])
.peek(row -> Arrays.fill(row, false)).toArray(boolean[][]::new);
int[] positions = new int[mappings.size()];
Arrays.fill(positions, 0);
// all the unprotected content (including unprotected content
// returned by the protection module and including content that
// didn't need to be unprotected)
String[][] allNewContents = IntStream.range(0, newContents.length).mapToObj(r -> {
String[] newRow = new String[allAttributeNames.length];
for (int c = 0; c < allAttributeNames.length; c++) {
String value;
String attributeName = allAttributeNames[c];
int idx = allAttributeNameIndexes.get(c).getValue();
if (idx != -1) {
String v = newContents[r][idx];
value = v != null && newContentFlags[r][idx] ? new String(v) : v;
newContentFlags[r][idx] = true;
} else {
int csp = IntStream.range(0, mappings.size())
.filter(i -> mappings.get(i).containsKey(attributeName)).findFirst().orElse(-1);
if (csp != -1) {
String protectedAttributeName = mappings.get(csp).get(attributeName);
List> panIndexes = allProtectedAttributeNameIndexes.get(csp);
panIndexes = panIndexes.subList(positions[csp], panIndexes.size());
idx = panIndexes.stream().filter(e -> e.getKey().equals(protectedAttributeName))
.map(Map.Entry::getValue).mapToInt(Integer::intValue).findFirst().getAsInt();
positions[csp] = idx + 1;
} else {
csp = 0;
List pans = Arrays.asList(allProtectedAttributeNames.get(csp));
pans = pans.subList(positions[csp], pans.size());
idx = pans.indexOf(attributeName);
positions[csp] = idx + 1;
}
value = allProtectedContents.get(csp)[r][idx];
}
newRow[c] = value;
}
return newRow;
}).toArray(String[][]::new);
// new values (preserving original values that were not modified)
List> newDataValues = IntStream.range(0, allNewContents.length)
.mapToObj(r -> Arrays.stream(allNewContents[r]).map(v -> {
CString dataValue = null;
boolean found = false;
loop1: for (int csp = 0; csp < allProtectedContents.size(); csp++) {
String[][] cspContent = allProtectedContents.get(csp);
if (r < cspContent.length) {
for (int c = 0; c < cspContent[r].length; c++) {
if (v == cspContent[r][c]) {
for (int i = 0; i < inboundDataOperations.size(); i++) {
InboundDataOperation inboundDataOperation = inboundDataOperations
.get(i);
if (inboundDataOperation.getInvolvedCSP() == csp) {
dataValue = inboundDataOperation.getDataValues().get(r).get(c);
found = true;
break loop1;
}
}
}
}
}
}
if (!found) {
dataValue = CString.valueOf(v);
}
return dataValue;
}).collect(Collectors.toList())).collect(Collectors.toList());
newInboundDataOperation = new InboundDataOperation();
newInboundDataOperation.setAttributes(inboundDataOperations.get(0).getAttributes());
newInboundDataOperation.setRequestId(inboundDataOperations.get(0).getRequestId());
newInboundDataOperation.setOperation(inboundDataOperations.get(0).getOperation());
newInboundDataOperation.setUsingHeadOperation(inboundDataOperations.get(0).isUsingHeadOperation());
newInboundDataOperation
.setUnprotectingDataEnabled(inboundDataOperations.get(0).isUnprotectingDataEnabled());
newInboundDataOperation.setDataIds(inboundDataOperations.get(0).getClearDataIds());
newInboundDataOperation.setDataValues(newDataValues);
List> dataIdMappings = mappings.stream()
.map(map -> map.entrySet().stream().collect(
Collectors.toMap(e -> CString.valueOf(e.getKey()), e -> CString.valueOf(e.getValue()))))
.collect(Collectors.toList());
newInboundDataOperation.setDataIdMappings(dataIdMappings);
newInboundDataOperation.setPromise(promise);
newInboundDataOperation.setModified(true);
newInboundDataOperation.setInvolvedCSPs(
inboundDataOperations.stream().map(DataOperation::getInvolvedCSPs).filter(csps -> csps != null)
.flatMap(List::stream).sorted().collect(Collectors.toList()));
if (newInboundDataOperation.getInvolvedCSPs().isEmpty()) {
newInboundDataOperation.setInvolvedCSPs(null);
}
}
}
if (newInboundDataOperation == null) {
// nothing to unprotect
if (inboundDataOperations.size() == 0) {
throw new IllegalStateException("strange");
}
newInboundDataOperation = new InboundDataOperation();
newInboundDataOperation.setAttributes(inboundDataOperations.get(0).getAttributes());
newInboundDataOperation.setRequestId(inboundDataOperations.get(0).getRequestId());
newInboundDataOperation.setOperation(inboundDataOperations.get(0).getOperation());
newInboundDataOperation.setUsingHeadOperation(inboundDataOperations.get(0).isUsingHeadOperation());
newInboundDataOperation
.setUnprotectingDataEnabled(inboundDataOperations.get(0).isUnprotectingDataEnabled());
newInboundDataOperation.setDataIds(inboundDataOperations.get(0).getClearDataIds());
newInboundDataOperation.setDataValues(inboundDataOperations.get(0).getDataValues());
List> dataIdMappings;
if (mappings.isEmpty()) {
dataIdMappings = Collections.singletonList(inboundDataOperations.get(0).getDataIds().stream().distinct()
.collect(Collectors.toMap(Function.identity(), Function.identity())));
} else {
dataIdMappings = Collections.singletonList(mappings.get(0).entrySet().stream().collect(
Collectors.toMap(e -> CString.valueOf(e.getKey()), e -> CString.valueOf(e.getValue()))));
}
newInboundDataOperation.setDataIdMappings(dataIdMappings);
newInboundDataOperation.setPromise(inboundDataOperations.get(0).getPromise());
newInboundDataOperation.setModified(true);
newInboundDataOperation.setInvolvedCSPs(inboundDataOperations.stream().map(DataOperation::getInvolvedCSPs)
.filter(csps -> csps != null).flatMap(List::stream).sorted().collect(Collectors.toList()));
if (newInboundDataOperation.getInvolvedCSPs().isEmpty()) {
newInboundDataOperation.setInvolvedCSPs(null);
}
}
return newInboundDataOperation;
}
private List newUpdateOperation(OutboundDataOperation outboundDataOperation) {
List newOutboundDataOperations = null;
// Filter attribute names according to the mapping
// (in order to pass only attributes that have to be protected)
String[] allAttributeNames = outboundDataOperation.getDataIds().stream().map(CString::toString)
.toArray(String[]::new);
List allCriterions = outboundDataOperation.getCriterions();
String[] allCriteriaAttributeNames = allCriterions.stream()
.map(criterion -> criterion != null ? criterion.getDataId().toString() : null).toArray(String[]::new);
List> mappings = head(Stream
.concat(Stream.of(allAttributeNames), Stream.of(allCriteriaAttributeNames).filter(can -> can != null))
.toArray(String[]::new));
int numberOfCSPs = mappings.size();
List protectedAttributes = Arrays.stream(allAttributeNames)
.map(an -> mappings.stream().anyMatch(map -> map.containsKey(an))).collect(Collectors.toList());
String[] attributeNames = IntStream.range(0, allAttributeNames.length).filter(i -> protectedAttributes.get(i))
.mapToObj(i -> allAttributeNames[i]).toArray(String[]::new);
Criteria[] allCriteria = allCriterions.stream()
.map(criterion -> criterion != null ? new Criteria(criterion.getDataId().toString(),
criterion.getOperator().toString(), criterion.getValue().toString()) : null)
.toArray(Criteria[]::new);
List protectedCriteria = Arrays.stream(allCriteriaAttributeNames)
.map(can -> can != null && mappings.stream().anyMatch(map -> map.containsKey(can)))
.collect(Collectors.toList());
Criteria[] criteria = IntStream.range(0, allCriteria.length).filter(i -> protectedCriteria.get(i))
.mapToObj(i -> allCriteria[i]).toArray(Criteria[]::new);
int[] criteriaMapping = IntStream.range(0, allCriteria.length)
.map(i -> protectedCriteria.get(i) ? Arrays.asList(criteria).indexOf(allCriteria[i]) : -1).toArray();
if (attributeNames.length > 0 || criteria.length > 0) {
// at least one attribute has to be protected
String[][] allContents;
String[][] contents;
String[][] newAttributeNames;
String[][][] newContents;
List> mappingIndexes;
Criteria[][] newCriteria;
if (outboundDataOperation.isUsingHeadOperation()) {
// using head operation is more efficient when there is no
// content to protect and no criteria
if (!outboundDataOperation.getDataValues().isEmpty()) {
throw new IllegalStateException("cannot use head operation to protect content");
}
if (allCriteria.length > 0) {
throw new IllegalStateException("cannot use head operation with criteria");
}
allContents = new String[0][];
contents = new String[0][];
newAttributeNames = Stream.generate(() -> attributeNames).limit(numberOfCSPs)
.toArray(String[][]::new);
newContents = Stream.generate(() -> contents).limit(numberOfCSPs)
.toArray(String[][][]::new);
mappingIndexes = Stream.>generate(() -> Collections.emptyMap()).limit(numberOfCSPs)
.collect(Collectors.toList());
newCriteria = Stream.generate(() -> criteria).limit(numberOfCSPs)
.toArray(Criteria[][]::new);
} else {
// using put operation allow to protect content
allContents = outboundDataOperation.getDataValues().stream()
.map(row -> row.stream().map(StringUtilities::toString).toArray(String[]::new))
.toArray(String[][]::new);
contents = Arrays.stream(allContents)
.map(allRow -> IntStream.range(0, protectedAttributes.size())
.filter(i -> protectedAttributes.get(i)).mapToObj(i -> allRow[i])
.toArray(String[]::new))
.toArray(String[][]::new);
List results = put(attributeNames, criteria, contents);
newAttributeNames = results == null
? Stream.generate(() -> attributeNames).limit(numberOfCSPs).toArray(String[][]::new)
: results.stream().map(DataOperationCommand::getProtectedAttributeNames)
.map(pans -> pans == null ? new String[0] : pans).toArray(String[][]::new);
newContents = results == null
? Stream.generate(() -> contents).limit(numberOfCSPs).toArray(String[][][]::new)
: results.stream().map(DataOperationCommand::getProtectedContents).toArray(String[][][]::new);
mappingIndexes = results == null
? Stream.>generate(() -> Collections.emptyMap()).limit(numberOfCSPs)
.collect(Collectors.toList())
: results.stream()
.map(result -> result.getMapping().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
e -> IntStream.range(0, result.getProtectedAttributeNames().length)
.filter(i -> result.getProtectedAttributeNames()[i]
.equals(e.getValue()))
.findFirst().getAsInt())))
.collect(Collectors.toList());
newCriteria = results == null
? Stream.generate(() -> criteria).limit(numberOfCSPs).toArray(Criteria[][]::new)
: results.stream().map(DataOperationCommand::getCriteria)
.map(cs -> cs == null ? new Criteria[0] : cs).toArray(Criteria[][]::new);
}
if (numberOfCSPs > 1 // more than one csp
// one csp but attribute names change
|| (numberOfCSPs == 1 && !mappings.get(0).entrySet().stream()
.allMatch(e -> e.getValue() == null || e.getKey().equals(e.getValue())))
// one csp but content change
|| (newContents.length == 1 && !Arrays.equals(contents, newContents[0]))
// one csp but criteria change
|| (newCriteria.length == 1 && !Arrays.equals(criteria, newCriteria[0]))) {
newOutboundDataOperations = new ArrayList<>(numberOfCSPs);
for (int csp = 0; csp < numberOfCSPs; csp++) {
if (outboundDataOperation.getInvolvedCSPs() != null
&& !outboundDataOperation.getInvolvedCSPs().contains(csp)) {
continue;
}
Map cspMapping = mappings.get(csp);
if (cspMapping.size() == 0 || newAttributeNames[csp].length == 0) {
continue;
}
Map cspMappingIndexes = mappingIndexes.get(csp);
OutboundDataOperation newOutboundDataOperation = new OutboundDataOperation();
newOutboundDataOperation.setAttributes(outboundDataOperation.getAttributes());
newOutboundDataOperation.setRequestId(outboundDataOperation.getRequestId());
newOutboundDataOperation.setOperation(outboundDataOperation.getOperation());
newOutboundDataOperation.setUsingHeadOperation(outboundDataOperation.isUsingHeadOperation());
newOutboundDataOperation
.setUnprotectingDataEnabled(outboundDataOperation.isUnprotectingDataEnabled());
List dataIds = new ArrayList<>(protectedAttributes.size());
for (int c = 0; c < protectedAttributes.size(); c++) {
if (protectedAttributes.get(c)) {
String newAttributeName = cspMapping.get(allAttributeNames[c]);
if (newAttributeName != null && Arrays.stream(newAttributeNames[csp])
.anyMatch(nan -> nan.equals(newAttributeName))) {
dataIds.add(CString.valueOf(newAttributeName));
}
} else {
dataIds.add(CString.valueOf(allAttributeNames[c]));
}
}
newOutboundDataOperation.setDataIds(dataIds);
String[][] cspNewContents = newContents[csp];
List> newDataValues = new ArrayList<>(allContents.length);
for (int r = 0; r < allContents.length; r++) {
List rowDataValues = new ArrayList<>(protectedAttributes.size());
for (int c = 0; c < protectedAttributes.size(); c++) {
if (protectedAttributes.get(c)) {
String newAttributeName = cspMapping.get(allAttributeNames[c]);
if (newAttributeName != null && Arrays.stream(newAttributeNames[csp])
.anyMatch(nan -> nan.equals(newAttributeName))) {
rowDataValues.add(CString
.valueOf(cspNewContents[r][cspMappingIndexes.get(allAttributeNames[c])]));
}
} else {
rowDataValues.add(CString.valueOf(allContents[r][c]));
}
}
newDataValues.add(rowDataValues);
}
newOutboundDataOperation.setDataValues(newDataValues);
Map dataIdMapping = cspMapping.entrySet().stream().collect(
Collectors.toMap(e -> CString.valueOf(e.getKey()), e -> CString.valueOf(e.getValue())));
newOutboundDataOperation.setDataIdMapping(dataIdMapping);
List criterions = new ArrayList<>(protectedCriteria.size());
for (int i = 0; i < protectedCriteria.size(); i++) {
if (protectedCriteria.get(i)) {
int idx = criteriaMapping[i];
if (idx != -1) {
criterions.add(new OutboundDataOperation.Criterion(
CString.valueOf(newCriteria[csp][idx].getAttributeName()),
CString.valueOf(newCriteria[csp][idx].getOperator()),
CString.valueOf(newCriteria[csp][idx].getValue())));
}
} else {
criterions.add(allCriterions.get(i));
}
}
newOutboundDataOperation.setCriterions(criterions);
newOutboundDataOperation.setModified(true);
newOutboundDataOperation.setInvolvedCSP(csp);
newOutboundDataOperations.add(newOutboundDataOperation);
}
}
}
if (newOutboundDataOperations == null) {
// nothing to protect
if (outboundDataOperation.getInvolvedCSPs() != null) {
// retain only the first CSP
outboundDataOperation.getInvolvedCSPs().removeIf(csp -> csp != 0);
}
if (outboundDataOperation.getInvolvedCSPs() == null || !outboundDataOperation.getInvolvedCSPs().isEmpty()) {
outboundDataOperation.setInvolvedCSP(0);
Map dataIdMapping = outboundDataOperation.getDataIds().stream().distinct()
.collect(Collectors.toMap(Function.identity(), Function.identity()));
outboundDataOperation.setDataIdMapping(dataIdMapping);
newOutboundDataOperations = Collections.singletonList(outboundDataOperation);
} else {
newOutboundDataOperations = Collections.emptyList();
}
}
return newOutboundDataOperations;
}
private List newDeleteOperation(OutboundDataOperation outboundDataOperation) {
List newOutboundDataOperations = null;
// Filter attribute names according to the mapping
// (in order to pass only attributes that have to be deleted)
String[] allAttributeNames = outboundDataOperation.getDataIds().stream().map(CString::toString)
.toArray(String[]::new);
List allCriterions = outboundDataOperation.getCriterions();
String[] allCriteriaAttributeNames = allCriterions.stream()
.map(criterion -> criterion != null ? criterion.getDataId().toString() : null).toArray(String[]::new);
List> mappings = head(Stream
.concat(Stream.of(allAttributeNames), Stream.of(allCriteriaAttributeNames).filter(can -> can != null))
.toArray(String[]::new));
int numberOfCSPs = mappings.size();
List fqAttributeNames = mappings.stream().map(Map::keySet).flatMap(Set::stream).distinct()
.collect(Collectors.toList());
String[] allFQAttributeNames = Arrays.stream(allAttributeNames).flatMap(attributeName -> {
Stream stream = Stream.of(attributeName);
if (attributeName.indexOf('*') != -1) {
Pattern pattern = Pattern.compile(escapeRegex(attributeName));
if (fqAttributeNames.stream().anyMatch(fqan -> pattern.matcher(fqan).matches())) {
stream = fqAttributeNames.stream().filter(fqan -> pattern.matcher(fqan).matches());
}
}
return stream;
}).toArray(String[]::new);
List protectedAttributes = Arrays.stream(allFQAttributeNames)
.map(an -> mappings.stream().anyMatch(map -> map.containsKey(an))).collect(Collectors.toList());
String[] attributeNames = IntStream.range(0, allFQAttributeNames.length).filter(i -> protectedAttributes.get(i))
.mapToObj(i -> allFQAttributeNames[i]).distinct().toArray(String[]::new);
Criteria[] allCriteria = allCriterions.stream()
.map(criterion -> criterion != null ? new Criteria(criterion.getDataId().toString(),
criterion.getOperator().toString(), criterion.getValue().toString()) : null)
.toArray(Criteria[]::new);
List protectedCriteria = Arrays.stream(allCriteriaAttributeNames)
.map(can -> can != null && mappings.stream().anyMatch(map -> map.containsKey(can)))
.collect(Collectors.toList());
Criteria[] criteria = IntStream.range(0, allCriteria.length).filter(i -> protectedCriteria.get(i))
.mapToObj(i -> allCriteria[i]).toArray(Criteria[]::new);
int[] criteriaMapping = IntStream.range(0, allCriteria.length)
.map(i -> protectedCriteria.get(i) ? Arrays.asList(criteria).indexOf(allCriteria[i]) : -1).toArray();
if (attributeNames.length > 0 || criteria.length > 0) {
// at least one attribute has to be deleted
String[][] newAttributeNames;
Criteria[][] newCriteria;
if (outboundDataOperation.isUsingHeadOperation()) {
// using head operation is more efficient when there is no
// criteria
if (allCriteria.length > 0) {
throw new IllegalStateException("cannot use head operation with criteria");
}
newAttributeNames = Stream.generate(() -> attributeNames).limit(numberOfCSPs)
.toArray(String[][]::new);
newCriteria = Stream.generate(() -> criteria).limit(numberOfCSPs)
.toArray(Criteria[][]::new);
} else {
// using delete operation allow to delete content
List results = delete(attributeNames, criteria);
newAttributeNames = results == null
? Stream.generate(() -> attributeNames).limit(numberOfCSPs).toArray(String[][]::new)
: results.stream().map(DataOperationCommand::getProtectedAttributeNames)
.map(pans -> pans == null ? new String[0] : pans).toArray(String[][]::new);
newCriteria = results == null ? new Criteria[numberOfCSPs][0]
: results.stream().map(DataOperationCommand::getCriteria)
.map(cs -> cs == null ? new Criteria[0] : cs).toArray(Criteria[][]::new);
}
if (numberOfCSPs > 1 // more than one csp
// one csp but attribute names change
|| (numberOfCSPs == 1 && !mappings.get(0).entrySet().stream()
.allMatch(e -> e.getValue() == null || e.getKey().equals(e.getValue())))
// one csp but criteria change
|| (newCriteria.length == 1 && !Arrays.equals(criteria, newCriteria[0]))) {
newOutboundDataOperations = new ArrayList<>(numberOfCSPs);
for (int csp = 0; csp < numberOfCSPs; csp++) {
if (outboundDataOperation.getInvolvedCSPs() != null
&& !outboundDataOperation.getInvolvedCSPs().contains(csp)) {
continue;
}
Map cspMapping = mappings.get(csp);
if (cspMapping.size() == 0 || newAttributeNames[csp].length == 0) {
continue;
}
OutboundDataOperation newOutboundDataOperation = new OutboundDataOperation();
newOutboundDataOperation.setAttributes(outboundDataOperation.getAttributes());
newOutboundDataOperation.setRequestId(outboundDataOperation.getRequestId());
newOutboundDataOperation.setOperation(outboundDataOperation.getOperation());
newOutboundDataOperation.setUsingHeadOperation(outboundDataOperation.isUsingHeadOperation());
newOutboundDataOperation
.setUnprotectingDataEnabled(outboundDataOperation.isUnprotectingDataEnabled());
List dataIds = new ArrayList<>(protectedAttributes.size());
for (int c = 0; c < protectedAttributes.size(); c++) {
if (protectedAttributes.get(c)) {
String newAttributeName = cspMapping.get(allFQAttributeNames[c]);
if (newAttributeName != null && Arrays.stream(newAttributeNames[csp])
.anyMatch(nan -> nan.equals(newAttributeName))) {
dataIds.add(CString.valueOf(newAttributeName));
}
} else {
dataIds.add(CString.valueOf(allFQAttributeNames[c]));
}
}
newOutboundDataOperation.setDataIds(dataIds);
Map dataIdMapping = cspMapping.entrySet().stream().collect(
Collectors.toMap(e -> CString.valueOf(e.getKey()), e -> CString.valueOf(e.getValue())));
newOutboundDataOperation.setDataIdMapping(dataIdMapping);
List criterions = new ArrayList<>(protectedCriteria.size());
for (int i = 0; i < protectedCriteria.size(); i++) {
if (protectedCriteria.get(i)) {
int idx = criteriaMapping[i];
if (idx != -1) {
criterions.add(new OutboundDataOperation.Criterion(
CString.valueOf(newCriteria[csp][idx].getAttributeName()),
CString.valueOf(newCriteria[csp][idx].getOperator()),
CString.valueOf(newCriteria[csp][idx].getValue())));
}
} else {
criterions.add(allCriterions.get(i));
}
}
newOutboundDataOperation.setCriterions(criterions);
newOutboundDataOperation.setModified(true);
newOutboundDataOperation.setInvolvedCSP(csp);
newOutboundDataOperations.add(newOutboundDataOperation);
}
}
}
if (newOutboundDataOperations == null) {
// nothing to protect
if (outboundDataOperation.getInvolvedCSPs() != null) {
// retain only the first CSP
outboundDataOperation.getInvolvedCSPs().removeIf(csp -> csp != 0);
}
if (outboundDataOperation.getInvolvedCSPs() == null || !outboundDataOperation.getInvolvedCSPs().isEmpty()) {
outboundDataOperation.setInvolvedCSP(0);
Map dataIdMapping = outboundDataOperation.getDataIds().stream().distinct()
.collect(Collectors.toMap(Function.identity(), Function.identity()));
outboundDataOperation.setDataIdMapping(dataIdMapping);
newOutboundDataOperations = Collections.singletonList(outboundDataOperation);
} else {
newOutboundDataOperations = Collections.emptyList();
}
}
return newOutboundDataOperations;
}
private List broadcastOutboundOperation(OutboundDataOperation outboundDataOperation) {
List newOutboundDataOperations;
if (outboundDataOperation.getInvolvedCSPs() != null && outboundDataOperation.getInvolvedCSPs().size() > 1) {
newOutboundDataOperations = outboundDataOperation.getInvolvedCSPs().stream().map(csp -> {
OutboundDataOperation newOutboundDataOperation = new OutboundDataOperation();
newOutboundDataOperation.setModified(true);
newOutboundDataOperation.setAttributes(outboundDataOperation.getAttributes());
newOutboundDataOperation.setInvolvedCSP(csp);
newOutboundDataOperation.setRequestId(outboundDataOperation.getRequestId());
newOutboundDataOperation.setOperation(outboundDataOperation.getOperation());
newOutboundDataOperation.setUsingHeadOperation(outboundDataOperation.isUsingHeadOperation());
newOutboundDataOperation.setDataIds(new ArrayList<>(outboundDataOperation.getDataIds()));
newOutboundDataOperation.setDataValues(new ArrayList<>(outboundDataOperation.getDataValues()));
Map dataIdMapping = outboundDataOperation.getDataIds().stream().distinct()
.collect(Collectors.toMap(Function.identity(), Function.identity()));
newOutboundDataOperation.setDataIdMapping(dataIdMapping);
newOutboundDataOperation
.setCriterions(
outboundDataOperation.getCriterions().stream()
.map(criterion -> new OutboundDataOperation.Criterion(criterion.getDataId(),
criterion.getOperator(), criterion.getValue()))
.collect(Collectors.toList()));
newOutboundDataOperation.setPromise(outboundDataOperation.getPromise());
newOutboundDataOperation.setUnprotectingDataEnabled(outboundDataOperation.isUnprotectingDataEnabled());
return newOutboundDataOperation;
}).collect(Collectors.toList());
} else {
newOutboundDataOperations = Collections.singletonList(outboundDataOperation);
}
return newOutboundDataOperations;
}
private List> head(String[] attributeNames) {
return protectionModule.getDataOperation().head(attributeNames);
}
private List get(String[] attributeNames, Criteria[] criteria) {
return protectionModule.getDataOperation().get(attributeNames, criteria);
}
private List get(List promise, List protectedContents) {
return protectionModule.getDataOperation().get(promise, protectedContents);
}
private List post(String[] attributeNames, String[][] contents) {
return protectionModule.getDataOperation().post(attributeNames, contents);
}
private List put(String[] attributeNames, Criteria[] criteria, String[][] contents) {
return protectionModule.getDataOperation().put(attributeNames, criteria, contents);
}
private List delete(String[] attributeNames, Criteria[] criteria) {
return protectionModule.getDataOperation().delete(attributeNames, criteria);
}
}