blob: b19d981b7c17fd190008673f1b2bdf5e99e564b6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.sysds.runtime.privacy;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.apache.sysds.parser.DataExpression;
import org.apache.sysds.runtime.privacy.finegrained.DataRange;
import org.apache.sysds.runtime.privacy.finegrained.FineGrainedPrivacy;
import org.apache.sysds.runtime.privacy.finegrained.FineGrainedPrivacyList;
import org.apache.wink.json4j.JSONArray;
import org.apache.wink.json4j.JSONException;
import org.apache.wink.json4j.JSONObject;
import org.apache.wink.json4j.OrderedJSONObject;
/**
* PrivacyConstraint holds all privacy constraints for data in the system at
* compile time and runtime.
*/
public class PrivacyConstraint implements Externalizable
{
public enum PrivacyLevel {
None, // No data exchange constraints. Data can be shared with anyone.
Private, // Data cannot leave the origin.
PrivateAggregation // Only aggregations of the data can leave the origin.
}
protected PrivacyLevel privacyLevel = PrivacyLevel.None;
protected FineGrainedPrivacy fineGrainedPrivacy;
/**
* Basic Constructor with a fine-grained collection
* based on a list implementation.
*/
public PrivacyConstraint(){
this(new FineGrainedPrivacyList());
}
/**
* Constructor with the option to choose between
* different fine-grained collection implementations.
* @param fineGrainedPrivacyCollection the instance in which fine-grained constraints are stored
*/
public PrivacyConstraint(FineGrainedPrivacy fineGrainedPrivacyCollection){
setFineGrainedPrivacyConstraints(fineGrainedPrivacyCollection);
}
/**
* Constructor with default fine-grained collection implementation
* where the entire data object is set to the given privacy level.
* @param privacyLevel for the entire data object.
*/
public PrivacyConstraint(PrivacyLevel privacyLevel) {
this();
setPrivacyLevel(privacyLevel);
}
public void setPrivacyLevel(PrivacyLevel privacyLevel){
this.privacyLevel = privacyLevel;
}
public PrivacyLevel getPrivacyLevel(){
return privacyLevel;
}
/**
* Checks if fine-grained privacy is set for this privacy constraint.
* @return true if the privacy constraint has fine-grained constraints.
*/
public boolean hasFineGrainedConstraints(){
return fineGrainedPrivacy.hasConstraints();
}
/**
* Sets fine-grained privacy for the privacy constraint.
* Existing fine-grained privacy collection will be overwritten.
* @param fineGrainedPrivacy fine-grained privacy instance which is set for the privacy constraint
*/
public void setFineGrainedPrivacyConstraints(FineGrainedPrivacy fineGrainedPrivacy){
this.fineGrainedPrivacy = fineGrainedPrivacy;
}
/**
* Get fine-grained privacy instance.
* @return fine-grained privacy instance
*/
public FineGrainedPrivacy getFineGrainedPrivacy(){
return fineGrainedPrivacy;
}
/**
* Return true if any of the elements has privacy level private
* @return true if any element has privacy level private
*/
public boolean hasPrivateElements(){
if (privacyLevel == PrivacyLevel.Private) return true;
if ( hasFineGrainedConstraints() ){
DataRange[] dataRanges = fineGrainedPrivacy.getDataRangesOfPrivacyLevel(PrivacyLevel.Private);
return dataRanges != null && dataRanges.length > 0;
} else return false;
}
/**
* Return true if any constraints have level Private or PrivateAggregate.
* @return true if any constraints have level Private or PrivateAggregate
*/
public boolean hasConstraints(){
if ( privacyLevel != null &&
(privacyLevel == PrivacyLevel.Private || privacyLevel == PrivacyLevel.PrivateAggregation) )
return true;
else if ( hasFineGrainedConstraints() ){
DataRange[] privateRanges = fineGrainedPrivacy.getDataRangesOfPrivacyLevel(PrivacyLevel.Private);
DataRange[] aggregateRanges = fineGrainedPrivacy.getDataRangesOfPrivacyLevel(PrivacyLevel.PrivateAggregation);
return (privateRanges != null && privateRanges.length > 0)
|| (aggregateRanges != null && aggregateRanges.length > 0);
} else return false;
}
/**
* Get privacy constraints and put them into JSON object.
* @param json JSON object in which the privacy constraints are put
* @return JSON object including the privacy constraints
* @throws JSONException in case of errors in creating JSON object
*/
public JSONObject toJson(JSONObject json) throws JSONException {
if ( getPrivacyLevel() != null && getPrivacyLevel() != PrivacyLevel.None )
json.put(DataExpression.PRIVACY, getPrivacyLevel().name());
if ( hasFineGrainedConstraints() ) {
DataRange[] privateRanges = getFineGrainedPrivacy().getDataRangesOfPrivacyLevel(PrivacyLevel.Private);
JSONArray privateRangesJson = getJsonArray(privateRanges);
DataRange[] aggregateRanges = getFineGrainedPrivacy().getDataRangesOfPrivacyLevel(PrivacyLevel.PrivateAggregation);
JSONArray aggregateRangesJson = getJsonArray(aggregateRanges);
OrderedJSONObject rangesJson = new OrderedJSONObject();
rangesJson.put(PrivacyLevel.Private.name(), privateRangesJson);
rangesJson.put(PrivacyLevel.PrivateAggregation.name(), aggregateRangesJson);
json.put(DataExpression.FINE_GRAINED_PRIVACY, rangesJson);
}
return json;
}
private static JSONArray getJsonArray(DataRange[] ranges) throws JSONException {
JSONArray rangeObjects = new JSONArray();
for ( DataRange range : ranges ){
List<Long> rangeBegin = Arrays.stream(range.getBeginDims()).boxed().collect(Collectors.toList());
List<Long> rangeEnd = Arrays.stream(range.getEndDims()).boxed().collect(Collectors.toList());
JSONArray beginJson = new JSONArray(rangeBegin);
JSONArray endJson = new JSONArray(rangeEnd);
JSONArray rangeObject = new JSONArray();
rangeObject.put(beginJson);
rangeObject.put(endJson);
rangeObjects.add(rangeObject);
}
return rangeObjects;
}
@Override
public void readExternal(ObjectInput is) throws IOException {
this.privacyLevel = PrivacyLevel.values()[is.readInt()];
int fineGrainedConstraintLength = is.readInt();
if ( fineGrainedConstraintLength > 0 ){
for (int i = 0; i < fineGrainedConstraintLength; i++){
Integer levelIndex = (Integer) is.readInt();
PrivacyLevel rangePrivacy = PrivacyLevel.values()[levelIndex];
DataRange dataRange = readExternalDataRangeObject(is);
fineGrainedPrivacy.put(dataRange, rangePrivacy);
}
}
}
@Override
public void writeExternal(ObjectOutput objectOutput) throws IOException {
objectOutput.writeInt(getPrivacyLevel().ordinal());
if (fineGrainedPrivacy != null && fineGrainedPrivacy.hasConstraints()){
List<Entry<DataRange,PrivacyLevel>> finegrainedConstraints = fineGrainedPrivacy.getAllConstraintsList();
objectOutput.writeInt(finegrainedConstraints.size());
for ( Entry<DataRange,PrivacyLevel> constraint : finegrainedConstraints ) {
objectOutput.writeInt(constraint.getValue().ordinal());
DataRange dataRange = constraint.getKey();
objectOutput.writeInt(dataRange.getBeginDims().length);
writeExternalRangeDim(objectOutput, dataRange.getBeginDims());
writeExternalRangeDim(objectOutput, dataRange.getEndDims());
}
}
else {
objectOutput.writeInt(0);
}
}
/**
* Reads a DataRange from ObjectInput.
* @param is ObjectInput from which the DataRange is read
* @return DataRange from ObjectInput
* @throws IOException
*/
private static DataRange readExternalDataRangeObject(ObjectInput is) throws IOException {
int dimLength = is.readInt();
long[] beginDims = readExternalDataRangeDim(is, dimLength);
long[] endDims = readExternalDataRangeDim(is, dimLength);
return new DataRange(beginDims, endDims);
}
/**
* Read a long array of the specified length from object input.
* @param is ObjectInput from which the long array is read
* @param dimLength length of input long array
* @return the input array as a long array
* @throws IOException
*/
private static long[] readExternalDataRangeDim(ObjectInput is, int dimLength) throws IOException {
long[] dims = new long[dimLength];
for(int i = 0; i < dimLength; i++){
dims[i] = is.readLong();
}
return dims;
}
/**
* Write the long array to ObjectOutput.
* @param objectOutput ObjectOutput in which the long array is written.
* @param rangeDim long array to write in ObjectOutput.
* @throws IOException
*/
private static void writeExternalRangeDim(ObjectOutput objectOutput, long[] rangeDim) throws IOException {
for ( long beginIndex : rangeDim ){
objectOutput.writeLong(beginIndex);
}
}
}