-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WOR-1596] Add data-tracking policy (#100)
- Loading branch information
1 parent
89e86a1
commit e7d90ce
Showing
5 changed files
with
379 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
service/src/main/java/bio/terra/policy/service/policy/PolicyDataTrackingConstraint.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package bio.terra.policy.service.policy; | ||
|
||
import bio.terra.policy.common.model.Constants; | ||
import bio.terra.policy.common.model.PolicyInput; | ||
import bio.terra.policy.common.model.PolicyName; | ||
import com.google.common.annotations.VisibleForTesting; | ||
import com.google.common.collect.ArrayListMultimap; | ||
import com.google.common.collect.Multimap; | ||
import java.util.Collection; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
public class PolicyDataTrackingConstraint extends PolicyBase { | ||
private static final String DATA_KEY = "dataType"; | ||
|
||
@Override | ||
public PolicyName getPolicyName() { | ||
return Constants.DATA_TRACKING_POLICY_NAME; | ||
} | ||
|
||
/** | ||
* Combine of data types - there is no conflict case as data can fall under multiple categories | ||
* (e.g. federally protected PHI). We simply create two Sets of data types from the | ||
* comma-separated form, then mash them together, and make them back into comma-separated form. | ||
* | ||
* @param dependent policy input | ||
* @param source policy input | ||
* @return policy input | ||
*/ | ||
@Override | ||
protected PolicyInput performCombine(PolicyInput dependent, PolicyInput source) { | ||
if (source == null) { | ||
return dependent; | ||
} | ||
|
||
if (dependent == null) { | ||
return source; | ||
} | ||
|
||
Set<String> dependentSet = dataToSet(dependent.getData(DATA_KEY)); | ||
Set<String> sourceSet = dataToSet(source.getData(DATA_KEY)); | ||
dependentSet.addAll(sourceSet); | ||
Multimap<String, String> newData = ArrayListMultimap.create(); | ||
dependentSet.forEach(type -> newData.put(DATA_KEY, type)); | ||
return new PolicyInput(dependent.getPolicyName(), newData); | ||
} | ||
|
||
/** | ||
* Remove data tracking | ||
* | ||
* @param target existing policy | ||
* @param removePolicy policy to remove | ||
* @return the target with dataTypes removed; null if no dataTypes left | ||
*/ | ||
@Override | ||
protected PolicyInput performRemove(PolicyInput target, PolicyInput removePolicy) { | ||
Set<String> targetDataTypes = dataToSet(target.getData(DATA_KEY)); | ||
Set<String> removeDataTypes = dataToSet(removePolicy.getData(DATA_KEY)); | ||
targetDataTypes.removeAll(removeDataTypes); | ||
|
||
if (targetDataTypes.isEmpty()) { | ||
return null; | ||
} | ||
|
||
Multimap<String, String> newData = ArrayListMultimap.create(); | ||
targetDataTypes.forEach(dataType -> newData.put(DATA_KEY, dataType)); | ||
return new PolicyInput(Constants.DATA_TRACKING_POLICY_NAME, newData); | ||
} | ||
|
||
/** | ||
* For dataTypes, the only thing we validate right now is that the key is correct. | ||
* | ||
* @param policyInput the input to validate | ||
* @return | ||
*/ | ||
@Override | ||
protected boolean performIsValid(PolicyInput policyInput) { | ||
var dataTypes = policyInput.getAdditionalData(); | ||
if (dataTypes.isEmpty()) { | ||
return false; | ||
} | ||
|
||
for (var entry : dataTypes.entries()) { | ||
if (!entry.getKey().equals(DATA_KEY)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
@VisibleForTesting | ||
Set<String> dataToSet(Collection<String> dataTypes) { | ||
return new HashSet<>(dataTypes); | ||
} | ||
} |
276 changes: 276 additions & 0 deletions
276
service/src/test/java/bio/terra/policy/service/policy/PolicyDataTrackingConstraintTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
package bio.terra.policy.service.policy; | ||
|
||
import static bio.terra.policy.service.policy.PolicyTestUtils.*; | ||
import static bio.terra.policy.testutils.PaoTestUtil.DATA_TRACKING_CONSTRAINT; | ||
import static bio.terra.policy.testutils.PaoTestUtil.DATA_TRACKING_KEY; | ||
import static bio.terra.policy.testutils.PaoTestUtil.DATA_TYPE_NAME; | ||
import static bio.terra.policy.testutils.PaoTestUtil.DATA_TYPE_NAME_ALT; | ||
import static bio.terra.policy.testutils.PaoTestUtil.REGION_KEY; | ||
import static bio.terra.policy.testutils.PaoTestUtil.TERRA_NAMESPACE; | ||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.contains; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertNull; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import bio.terra.policy.common.model.PolicyInput; | ||
import bio.terra.policy.testutils.TestUnitBase; | ||
import com.google.common.collect.ArrayListMultimap; | ||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class PolicyDataTrackingConstraintTest extends TestUnitBase { | ||
@Test | ||
void dataTrackingConstraintTest_combineSameDataTypes() throws Exception { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
var dependentPolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, | ||
DATA_TRACKING_CONSTRAINT, | ||
buildMultimap(DATA_TRACKING_KEY, DATA_TYPE_NAME)); | ||
var sourcePolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, | ||
DATA_TRACKING_CONSTRAINT, | ||
buildMultimap(DATA_TRACKING_KEY, DATA_TYPE_NAME)); | ||
|
||
PolicyInput resultPolicy = dataTrackingConstraint.combine(dependentPolicy, sourcePolicy); | ||
|
||
Set<String> dataTypeSet = | ||
dataTrackingConstraint.dataToSet(resultPolicy.getAdditionalData().get(DATA_TRACKING_KEY)); | ||
|
||
assertEquals(1, dataTypeSet.size(), "Contains 1 dataType"); | ||
assertThat(dataTypeSet, contains(DATA_TYPE_NAME)); | ||
} | ||
|
||
@Test | ||
void dataTrackingConstraintTest_combineEmptySource() throws Exception { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
var dependentPolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, | ||
DATA_TRACKING_CONSTRAINT, | ||
buildMultimap(DATA_TRACKING_KEY, DATA_TYPE_NAME)); | ||
|
||
PolicyInput resultPolicy = dataTrackingConstraint.combine(dependentPolicy, null); | ||
|
||
Set<String> dataTypeSet = | ||
dataTrackingConstraint.dataToSet(resultPolicy.getAdditionalData().get(DATA_TRACKING_KEY)); | ||
|
||
assertEquals(1, dataTypeSet.size(), "Contains 1 dataType"); | ||
assertThat(dataTypeSet, contains(DATA_TYPE_NAME)); | ||
} | ||
|
||
@Test | ||
void dataTrackingConstraintTest_combineEmptyDestination() throws Exception { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
var sourcePolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, | ||
DATA_TRACKING_CONSTRAINT, | ||
buildMultimap(DATA_TRACKING_KEY, DATA_TYPE_NAME)); | ||
|
||
PolicyInput resultPolicy = dataTrackingConstraint.combine(null, sourcePolicy); | ||
Set<String> dataTypeSet = | ||
dataTrackingConstraint.dataToSet(resultPolicy.getAdditionalData().get(DATA_TRACKING_KEY)); | ||
|
||
assertEquals(1, dataTypeSet.size(), "Contains 1 dataType"); | ||
assertThat(dataTypeSet, contains(DATA_TYPE_NAME)); | ||
} | ||
|
||
@Test | ||
void dataTrackingConstraintTest_combineEmptySourceAndDestination() throws Exception { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
var dependentPolicy = | ||
new PolicyInput(TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(REGION_KEY)); | ||
var sourcePolicy = | ||
new PolicyInput(TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(REGION_KEY)); | ||
|
||
PolicyInput resultPolicy = dataTrackingConstraint.combine(dependentPolicy, sourcePolicy); | ||
|
||
Set<String> dataTypeSet = | ||
dataTrackingConstraint.dataToSet(resultPolicy.getAdditionalData().get(DATA_TRACKING_KEY)); | ||
|
||
assertEquals(0, dataTypeSet.size()); | ||
} | ||
|
||
@Test | ||
void dataTrackingConstraintTest_combineMismatchDataTypes() throws Exception { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
var dependentPolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, | ||
DATA_TRACKING_CONSTRAINT, | ||
buildMultimap(DATA_TRACKING_KEY, DATA_TYPE_NAME)); | ||
var sourcePolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, | ||
DATA_TRACKING_CONSTRAINT, | ||
buildMultimap(DATA_TRACKING_KEY, DATA_TYPE_NAME_ALT)); | ||
|
||
PolicyInput resultPolicy = dataTrackingConstraint.combine(dependentPolicy, sourcePolicy); | ||
Set<String> dataTypeSet = | ||
dataTrackingConstraint.dataToSet(resultPolicy.getAdditionalData().get(DATA_TRACKING_KEY)); | ||
|
||
assertEquals(2, dataTypeSet.size()); | ||
assertTrue(dataTypeSet.containsAll(Arrays.asList(DATA_TYPE_NAME, DATA_TYPE_NAME_ALT))); | ||
} | ||
|
||
@Test | ||
void dataTrackingConstraintTest_combineMultipleDataTypes() throws Exception { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
String dataType2 = DATA_TYPE_NAME + "2"; | ||
String dataType3 = DATA_TYPE_NAME + "3"; | ||
|
||
Set<String> dataTypes = new HashSet<>(Arrays.asList(DATA_TYPE_NAME, dataType2, dataType3)); | ||
|
||
var dependentPolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
var sourcePolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
|
||
PolicyInput resultPolicy = dataTrackingConstraint.combine(dependentPolicy, sourcePolicy); | ||
Set<String> dataTypeSet = | ||
dataTrackingConstraint.dataToSet(resultPolicy.getAdditionalData().get(DATA_TRACKING_KEY)); | ||
|
||
assertEquals(dataTypes.size(), dataTypeSet.size()); | ||
assertTrue(dataTypeSet.containsAll(dataTypes)); | ||
} | ||
|
||
@Test | ||
void dataTrackingConstraintTest_combineWhenDestinationHasFewerDataTypes() throws Exception { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
String dataType2 = DATA_TYPE_NAME + "2"; | ||
String dataType3 = DATA_TYPE_NAME + "3"; | ||
|
||
Set<String> dataTypes = new HashSet<>(Arrays.asList(DATA_TYPE_NAME, dataType2)); | ||
|
||
var dependentPolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
|
||
dataTypes.add(dataType3); | ||
var sourcePolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
|
||
PolicyInput resultPolicy = dataTrackingConstraint.combine(dependentPolicy, sourcePolicy); | ||
Set<String> dataTypeSet = | ||
dataTrackingConstraint.dataToSet(resultPolicy.getAdditionalData().get(DATA_TRACKING_KEY)); | ||
|
||
assertEquals(dataTypes.size(), dataTypeSet.size()); | ||
assertTrue(dataTypeSet.containsAll(dataTypes)); | ||
} | ||
|
||
@Test | ||
void dataTrackingConstraintTest_combineWhenSourceHasFewerDataTypes() throws Exception { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
String dataType2 = DATA_TYPE_NAME + "2"; | ||
String dataType3 = DATA_TYPE_NAME + "3"; | ||
|
||
Set<String> dataTypes = new HashSet<>(Arrays.asList(DATA_TYPE_NAME, dataType2, dataType3)); | ||
|
||
var dependentPolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
|
||
dataTypes.remove(dataType3); | ||
var sourcePolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
|
||
PolicyInput resultPolicy = dataTrackingConstraint.combine(dependentPolicy, sourcePolicy); | ||
Set<String> dataTypeSet = | ||
dataTrackingConstraint.dataToSet(resultPolicy.getAdditionalData().get(DATA_TRACKING_KEY)); | ||
|
||
assertEquals(3, dataTypeSet.size()); | ||
assertTrue(dataTypeSet.containsAll(dataTypes)); | ||
} | ||
|
||
@Test | ||
void dataTrackingConstraintTest_removeDataType() throws Exception { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
String dataType2 = DATA_TYPE_NAME + "2"; | ||
String dataType3 = DATA_TYPE_NAME + "3"; | ||
|
||
Set<String> dataTypes = new HashSet<>(Arrays.asList(DATA_TYPE_NAME)); | ||
var removePolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
|
||
dataTypes.add(dataType2); | ||
dataTypes.add(dataType3); | ||
var targetPolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
|
||
PolicyInput resultPolicy = dataTrackingConstraint.remove(targetPolicy, removePolicy); | ||
Set<String> dataTypeSet = | ||
dataTrackingConstraint.dataToSet(resultPolicy.getAdditionalData().get(DATA_TRACKING_KEY)); | ||
|
||
assertEquals(2, dataTypeSet.size()); | ||
assertTrue(dataTypeSet.containsAll(Arrays.asList(dataType2, dataType3))); | ||
} | ||
|
||
@Test | ||
void dataTrackingConstraintTest_removeAllDataTypes() throws Exception { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
String dataType2 = DATA_TYPE_NAME + "2"; | ||
String dataType3 = DATA_TYPE_NAME + "3"; | ||
|
||
Set<String> dataTypes = new HashSet<>(Arrays.asList(DATA_TYPE_NAME, dataType2, dataType3)); | ||
var removePolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
|
||
var targetPolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
|
||
PolicyInput resultPolicy = dataTrackingConstraint.remove(targetPolicy, removePolicy); | ||
assertNull(resultPolicy); | ||
} | ||
|
||
@Test | ||
void dataTrackingConstraintTest_validation() { | ||
var dataTrackingConstraint = new PolicyDataTrackingConstraint(); | ||
|
||
Set<String> dataTypes = new HashSet<>(Arrays.asList(DATA_TYPE_NAME)); | ||
var validPolicy = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
var emptyPolicy = | ||
new PolicyInput(TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, ArrayListMultimap.create()); | ||
var invalidKey = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, | ||
DATA_TRACKING_CONSTRAINT, | ||
buildMultimap(DATA_TRACKING_KEY + "invalid", dataTypes)); | ||
|
||
dataTypes.add("invalid"); | ||
var invalidValue = | ||
new PolicyInput( | ||
TERRA_NAMESPACE, DATA_TRACKING_CONSTRAINT, buildMultimap(DATA_TRACKING_KEY, dataTypes)); | ||
|
||
assertTrue(dataTrackingConstraint.isValid(validPolicy)); | ||
assertFalse(dataTrackingConstraint.isValid(emptyPolicy)); | ||
assertFalse(dataTrackingConstraint.isValid(invalidKey)); | ||
// we don't currently validate the value | ||
assertTrue(dataTrackingConstraint.isValid(invalidValue)); | ||
} | ||
} |
Oops, something went wrong.