Skip to content

Commit

Permalink
Add MapFailureStrategy option that will control the behaviour of MapF…
Browse files Browse the repository at this point in the history
…unc and MapAndEncryptFunc. The change will also influence classes that implements the Mapper interface. Remove MappingNotFoundException from the Mapper interface since it should no longer be used for mapping failures.
  • Loading branch information
Skaar, Bjørn-Andre committed Apr 8, 2024
1 parent d69bb7e commit 6e02f79
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ private PseudoFuncOutput transform(PseudoFuncInput input,
Function<PseudoFuncInput, PseudoFuncOutput> inner,
Function<PseudoFuncInput, PseudoFuncOutput> outer) {
final PseudoFuncOutput innerOutput = inner.apply(input);
if (innerOutput.getValue() == null) {
return innerOutput;
}
final PseudoFuncOutput outerOutput = outer.apply(
PseudoFuncInput.of(innerOutput.getValue()));
innerOutput.getWarnings().forEach(outerOutput::addWarning);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package no.ssb.dapla.dlp.pseudo.func.map;

public enum MapFailureStrategy {
RETURN_NULL,
RETURN_ORIGINAL
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
public class MapFuncConfig {
private final String context;
private final String snapshotDate;
private final MapFailureStrategy mapFailureStrategy;

@UtilityClass
public static class Param {
public static final String CONTEXT = "context";
public static final String SNAPSHOT_DATE = "snapshotDate";
public static final String MAP_FAILURE_STRATEGY = "failureStrategy";
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package no.ssb.dapla.dlp.pseudo.func.map;
import no.ssb.dapla.dlp.pseudo.func.PseudoFuncConfig;

import static no.ssb.dapla.dlp.pseudo.func.map.MapFuncConfig.Param.MAP_FAILURE_STRATEGY;
import static no.ssb.dapla.dlp.pseudo.func.map.MapFuncConfig.Param.SNAPSHOT_DATE;

public class MapFuncConfigService {
Expand All @@ -8,6 +10,8 @@ public MapFuncConfig resolve(PseudoFuncConfig genericConfig) {

return MapFuncConfig.builder()
.snapshotDate(genericConfig.get(SNAPSHOT_DATE, String.class).orElse(null))
.mapFailureStrategy(genericConfig.get(MAP_FAILURE_STRATEGY, MapFailureStrategy.class)
.orElse(MapFailureStrategy.RETURN_ORIGINAL))
.context(context)
.build();
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/no/ssb/dapla/dlp/pseudo/func/map/Mapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ public interface Mapper {

void init(PseudoFuncInput data);
void setConfig(Map<String, Object> config);
PseudoFuncOutput map(PseudoFuncInput data) throws MappingNotFoundException;
PseudoFuncOutput map(PseudoFuncInput data);

PseudoFuncOutput restore(PseudoFuncInput mapped) throws MappingNotFoundException;
PseudoFuncOutput restore(PseudoFuncInput mapped);

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
import no.ssb.dapla.dlp.pseudo.func.PseudoFuncOutput;
import no.ssb.dapla.dlp.pseudo.func.fpe.FpeFunc;
import no.ssb.dapla.dlp.pseudo.func.fpe.FpeFuncConfig;
import no.ssb.dapla.dlp.pseudo.func.map.MapFailureStrategy;
import no.ssb.dapla.dlp.pseudo.func.map.MapFunc;
import no.ssb.dapla.dlp.pseudo.func.map.MappingNotFoundException;
import no.ssb.dapla.dlp.pseudo.func.map.MapFuncConfig;
import no.ssb.dapla.dlp.pseudo.func.map.TestMapper;
import org.junit.jupiter.api.Test;

Expand All @@ -19,24 +20,25 @@
public class MapAndEncryptFuncTest {
private static final String BASE64_ENCODED_KEY = "w0/G6A5e/KHtTo31FD6mhhS1Tkga43l79IBK24gM4F8=";

final PseudoFuncConfig config = new PseudoFuncConfig(ImmutableMap.of(
PseudoFuncConfig.Param.FUNC_DECL, "map-fpe-test",
PseudoFuncConfig.Param.FUNC_IMPL, MapAndEncryptFunc.class.getName(),
MapAndEncryptFuncConfig.Param.MAP_FUNC_IMPL, MapFunc.class.getName(),
MapAndEncryptFuncConfig.Param.ENCRYPTION_FUNC_IMPL, FpeFunc.class.getName(),
FpeFuncConfig.Param.ALPHABET, "alphanumeric+whitespace",
FpeFuncConfig.Param.KEY_ID, "keyId1",
FpeFuncConfig.Param.KEY_DATA, BASE64_ENCODED_KEY

));
private PseudoFuncConfig getConfig() {
return new PseudoFuncConfig(ImmutableMap.of(
PseudoFuncConfig.Param.FUNC_DECL, "map-fpe-test",
PseudoFuncConfig.Param.FUNC_IMPL, MapAndEncryptFunc.class.getName(),
MapAndEncryptFuncConfig.Param.MAP_FUNC_IMPL, MapFunc.class.getName(),
MapAndEncryptFuncConfig.Param.ENCRYPTION_FUNC_IMPL, FpeFunc.class.getName(),
FpeFuncConfig.Param.ALPHABET, "alphanumeric+whitespace",
FpeFuncConfig.Param.KEY_ID, "keyId1",
FpeFuncConfig.Param.KEY_DATA, BASE64_ENCODED_KEY
));
}
/*
* This test should transform OriginalValue -> MappedValue -> FPE encrypted MappedValue
* (and back)
*/
@Test
public void transformAndRestore() {
String expectedVal = "ygd1M9at1nK"; // FPE encrypted MappedValue
PseudoFunc func = PseudoFuncFactory.create(config);
PseudoFunc func = PseudoFuncFactory.create(getConfig());

PseudoFuncOutput pseudonymized = func.apply(PseudoFuncInput.of(TestMapper.ORIGINAL));
assertThat(pseudonymized.getValue()).isEqualTo(expectedVal);
Expand All @@ -45,22 +47,41 @@ public void transformAndRestore() {
assertThat(depseudonymized.getValue()).isEqualTo(TestMapper.ORIGINAL);
}

@Test
public void transformAndRestoreWithMissingStrategyIgnore() {
// Using MapFailureStrategy.IGNORE one can successfully map and encrypt "gibberish"
// and get the same decrypted result back - although the Mapper function didn't find
// a mapping for "gibberish"
String originalVal = "gibberish";
String expectedVal = "J0LjYPO9f"; // FPE encrypted originalVal
final PseudoFuncConfig config = getConfig();
config.add(MapFuncConfig.Param.MAP_FAILURE_STRATEGY, MapFailureStrategy.RETURN_ORIGINAL);
PseudoFunc func = PseudoFuncFactory.create(config);

PseudoFuncOutput pseudonymized = func.apply(PseudoFuncInput.of(originalVal));
assertThat(pseudonymized.getValue()).isEqualTo(expectedVal);

PseudoFuncOutput depseudonymized = func.restore(PseudoFuncInput.of(pseudonymized.getValue()));
assertThat(depseudonymized.getValue()).isEqualTo(originalVal);
}


@Test
public void call_apply_with_mapping_failure() {
final PseudoFuncConfig config = getConfig();
config.add(MapFuncConfig.Param.MAP_FAILURE_STRATEGY, MapFailureStrategy.RETURN_NULL);
PseudoFunc func = PseudoFuncFactory.create(config);

assertThrows(MappingNotFoundException.class,() ->
func.apply(PseudoFuncInput.of("unknown"))
);
assertThat(func.apply(PseudoFuncInput.of("unknown")).getValue()).isNull();
}

@Test
public void call_restore_with_mapping_failure() {
final PseudoFuncConfig config = getConfig();
config.add(MapFuncConfig.Param.MAP_FAILURE_STRATEGY, MapFailureStrategy.RETURN_NULL);
PseudoFunc func = PseudoFuncFactory.create(config);

assertThrows(MappingNotFoundException.class,() ->
func.restore(PseudoFuncInput.of("unknown"))
);
assertThat(func.restore(PseudoFuncInput.of("unknown")).getValue()).isNull();
}

}
24 changes: 24 additions & 0 deletions src/test/java/no/ssb/dapla/dlp/pseudo/func/map/MapFuncTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,28 @@ public void transformAndRestore() {
assertThat(depseudonymized.getValue()).isEqualTo(TestMapper.ORIGINAL);
}

@Test
public void testInvalidMappingStrategyIgnore() {
final PseudoFuncConfig config = new PseudoFuncConfig(ImmutableMap.of(
PseudoFuncConfig.Param.FUNC_DECL, "map-test",
PseudoFuncConfig.Param.FUNC_IMPL, MapFunc.class.getName(),
MapFuncConfig.Param.MAP_FAILURE_STRATEGY, MapFailureStrategy.RETURN_ORIGINAL
));
PseudoFunc func = PseudoFuncFactory.create(config);

PseudoFuncOutput mapOutput = func.apply(PseudoFuncInput.of("nomappingavailable"));
assertThat(mapOutput.getValue()).isEqualTo("nomappingavailable");
}

@Test
public void testInvalidMappingStrategyAbort() {
final PseudoFuncConfig config = new PseudoFuncConfig(ImmutableMap.of(
PseudoFuncConfig.Param.FUNC_DECL, "map-test",
PseudoFuncConfig.Param.FUNC_IMPL, MapFunc.class.getName(),
MapFuncConfig.Param.MAP_FAILURE_STRATEGY, MapFailureStrategy.RETURN_NULL
));
PseudoFunc func = PseudoFuncFactory.create(config);

assertThat(func.apply(PseudoFuncInput.of("unknown")).getValue()).isNull();
}
}
17 changes: 13 additions & 4 deletions src/test/java/no/ssb/dapla/dlp/pseudo/func/map/TestMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import no.ssb.dapla.dlp.pseudo.func.PseudoFuncOutput;

import java.util.Map;
import java.util.Optional;

/**
* This class is loaded using the Java Service Provider API.
Expand All @@ -12,30 +13,38 @@ public class TestMapper implements Mapper {

public static String ORIGINAL = "OriginalValue";
public static String MAPPED = "MappedValue";
private MapFailureStrategy mapFailureStrategy = MapFailureStrategy.RETURN_ORIGINAL;

@Override
public void init(PseudoFuncInput data) {
}

@Override
public void setConfig(Map<String, Object> config) {
this.mapFailureStrategy = Optional.ofNullable(
config.getOrDefault(MapFuncConfig.Param.MAP_FAILURE_STRATEGY, null)
).map(String::valueOf).map(MapFailureStrategy::valueOf).orElse(MapFailureStrategy.RETURN_ORIGINAL);
}

@Override
public PseudoFuncOutput map(PseudoFuncInput input) throws MappingNotFoundException {
public PseudoFuncOutput map(PseudoFuncInput input) {
if (ORIGINAL.equals(input.value())) {
return PseudoFuncOutput.of(MAPPED);
} else if (mapFailureStrategy == MapFailureStrategy.RETURN_ORIGINAL) {
return PseudoFuncOutput.of(input.value());
} else {
throw new MappingNotFoundException(String.format("Could not map value %s", input.value()));
return PseudoFuncOutput.of(null);
}
}

@Override
public PseudoFuncOutput restore(PseudoFuncInput mapped) throws MappingNotFoundException {
public PseudoFuncOutput restore(PseudoFuncInput mapped) {
if (MAPPED.equals(mapped.value())) {
return PseudoFuncOutput.of(ORIGINAL);
} else if (mapFailureStrategy == MapFailureStrategy.RETURN_ORIGINAL) {
return PseudoFuncOutput.of(mapped.value());
} else {
throw new MappingNotFoundException(String.format("Could not map value %s", mapped.value()));
return PseudoFuncOutput.of(null);
}
}
}

0 comments on commit 6e02f79

Please sign in to comment.