From d9873816e0b58e7c95b09c415f4878a35223acea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn-Andre=20Skaar?= <31540110+bjornandre@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:53:32 +0100 Subject: [PATCH] Changed PseudoFuncInput and PseudoFuncOutput to only carry a single value. (#18) --- .../dlp/pseudo/func/PseudoFuncInput.java | 46 +------------ .../dlp/pseudo/func/PseudoFuncOutput.java | 41 ++--------- .../dapla/dlp/pseudo/func/fpe/FpeFunc.java | 69 ++++++++----------- .../dlp/pseudo/func/redact/RedactFunc.java | 32 +++------ .../pseudo/func/tink/daead/TinkDaeadFunc.java | 47 +++++-------- .../dlp/pseudo/func/tink/fpe/TinkFpeFunc.java | 43 +++++------- .../dlp/pseudo/func/PseudoFuncInputTest.java | 25 ------- .../dlp/pseudo/func/PseudoFuncOutputTest.java | 25 ------- .../dlp/pseudo/func/fpe/FpeFuncTest.java | 50 +++----------- .../func/tink/daead/TinkDaeadFuncTest.java | 42 +++-------- .../pseudo/func/tink/fpe/TinkFpeFuncTest.java | 20 +++--- 11 files changed, 111 insertions(+), 329 deletions(-) delete mode 100644 src/test/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncInputTest.java delete mode 100644 src/test/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncOutputTest.java diff --git a/src/main/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncInput.java b/src/main/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncInput.java index ee2fe42..67ba686 100644 --- a/src/main/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncInput.java +++ b/src/main/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncInput.java @@ -1,47 +1,7 @@ package no.ssb.dapla.dlp.pseudo.func; -import lombok.Value; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -@Value -public class PseudoFuncInput { - private final List values = new ArrayList<>(); - - public PseudoFuncInput(Object v) { - if (v instanceof Collection) { - values.addAll((Collection) v); - } - else if (v.getClass().isArray()) { - values.addAll(Arrays.asList((Object[]) v)); - } - else { - values.add(v); - } - } - - public static PseudoFuncInput of(Object v) { - return new PseudoFuncInput(v); - } - - public List getStringValues() { - return values.stream() - .map(String::valueOf) - .collect(Collectors.toList()); - } - - public List getParamMetadata() { - return getStringValues().stream() - .map(ParamMetadata::parse) - .collect(Collectors.toList()); - } - - @Override - public String toString() { - return String.valueOf(values); +public record PseudoFuncInput(String value) { + public static PseudoFuncInput of(String value) { + return new PseudoFuncInput(value); } } diff --git a/src/main/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncOutput.java b/src/main/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncOutput.java index 0ae3596..58b82fa 100644 --- a/src/main/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncOutput.java +++ b/src/main/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncOutput.java @@ -1,42 +1,24 @@ package no.ssb.dapla.dlp.pseudo.func; -import lombok.NoArgsConstructor; import lombok.Value; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; @Value -@NoArgsConstructor public class PseudoFuncOutput { - List values = new ArrayList<>(); + String value; List warnings = new ArrayList<>(); Map metadata = new HashMap<>(); - - public PseudoFuncOutput(Object v) { - if (v instanceof Collection) { - values.addAll((Collection) v); - } - else if (v.getClass().isArray()) { - values.addAll(Arrays.asList((Object[]) v)); - } - else { - values.add(v); - } - } - - public static PseudoFuncOutput of(Object v) { - return new PseudoFuncOutput(v); + public PseudoFuncOutput(String value) { + this.value = value; } - public void add(Object object) { - values.add(object); + public static PseudoFuncOutput of(String value) { + return new PseudoFuncOutput(value); } public void addWarning(String warning) { @@ -46,20 +28,9 @@ public void addWarning(String warning) { public void addMetadata(String key, String value) { metadata.put(key, value); } - - public List getStringValues() { - return values.stream() - .map(String::valueOf) - .collect(Collectors.toList()); - } - - public Object getFirstValue() { - return getValues().getFirst(); - } - @Override public String toString() { - return String.valueOf(values); + return String.valueOf(value); } public boolean hasWarnings() { diff --git a/src/main/java/no/ssb/dapla/dlp/pseudo/func/fpe/FpeFunc.java b/src/main/java/no/ssb/dapla/dlp/pseudo/func/fpe/FpeFunc.java index d0f298c..54985de 100644 --- a/src/main/java/no/ssb/dapla/dlp/pseudo/func/fpe/FpeFunc.java +++ b/src/main/java/no/ssb/dapla/dlp/pseudo/func/fpe/FpeFunc.java @@ -4,7 +4,9 @@ import com.idealista.fpe.builder.FormatPreservingEncryptionBuilder; import lombok.extern.slf4j.Slf4j; import no.ssb.dapla.dlp.pseudo.func.*; -import no.ssb.dapla.dlp.pseudo.func.util.FromString; + +import java.util.ArrayList; +import java.util.List; import static no.ssb.dapla.dlp.pseudo.func.fpe.Domains.domainOf; @@ -30,58 +32,43 @@ public FpeFunc(PseudoFuncConfig genericConfig) { config = fpeConfigService.resolve(genericConfig); fpe = FormatPreservingEncryptionBuilder - .ff1Implementation() - .withDomain(domainOf(config.getAlphabet())) - .withDefaultPseudoRandomFunction(config.getKey()) - .withDefaultLengthRange() - .build(); + .ff1Implementation() + .withDomain(domainOf(config.getAlphabet())) + .withDefaultPseudoRandomFunction(config.getKey()) + .withDefaultLengthRange() + .build(); } @Override public PseudoFuncOutput apply(PseudoFuncInput input) { - PseudoFuncOutput output = new PseudoFuncOutput(); - - for (Object inputValue : input.getValues()) { - String plain = String.valueOf(inputValue); - - if (config.isReplaceIllegalChars()) { - CharReplacer.ReplacementResult res = CharReplacer.replace(plain, config.getReplaceIllegalCharsWith(), config.getAlphabet().availableCharacters()); - if (res.hasReplacedChars()) { - plain = res.getResult(); - output.addWarning(String.format("%s --> %s replaced by '%s' before pseudo", - getFuncDecl(), res.getReplacedChars(), res.getReplacementString())); - } - } - final String pseudonymized; - try { - pseudonymized = isFpeTransformCompliant(plain) - ? fpe.encrypt(plain, STATIC_TWEAK) - : plain; - } - catch (Exception e) { - throw new FpePseudoFuncException("FPE pseudo apply error. func=" + getFuncDecl() + ", contentType=" + input.getParamMetadata(), e); + String plain = input.value(); + List warnings = new ArrayList<>(); + if (config.isReplaceIllegalChars()) { + CharReplacer.ReplacementResult res = CharReplacer.replace(plain, config.getReplaceIllegalCharsWith(), config.getAlphabet().availableCharacters()); + if (res.hasReplacedChars()) { + plain = res.getResult(); + warnings.add(String.format("%s --> %s replaced by '%s' before pseudo", + getFuncDecl(), res.getReplacedChars(), res.getReplacementString())); } - - output.add(FromString.convert(pseudonymized, inputValue.getClass())); } - - return output; + try { + final String pseudonymized = isFpeTransformCompliant(plain) ? fpe.encrypt(plain, STATIC_TWEAK) : plain; + PseudoFuncOutput output = PseudoFuncOutput.of(pseudonymized); + warnings.forEach(output::addWarning); + return output; + } catch (Exception e) { + throw new FpePseudoFuncException("FPE pseudo apply error. func=" + getFuncDecl(), e); + } } @Override public PseudoFuncOutput restore(PseudoFuncInput input) { - PseudoFuncOutput output = new PseudoFuncOutput(); - - for (Object inputValue : input.getValues()) { - String pseudonymized = String.valueOf(inputValue); + String pseudonymized = input.value(); String plain = isFpeTransformCompliant(pseudonymized) - ? fpe.decrypt(pseudonymized, STATIC_TWEAK) - : pseudonymized; - output.add(FromString.convert(plain, inputValue.getClass())); - } - - return output; + ? fpe.decrypt(pseudonymized, STATIC_TWEAK) + : pseudonymized; + return PseudoFuncOutput.of(plain); } /** diff --git a/src/main/java/no/ssb/dapla/dlp/pseudo/func/redact/RedactFunc.java b/src/main/java/no/ssb/dapla/dlp/pseudo/func/redact/RedactFunc.java index e361fe7..0b0ed40 100644 --- a/src/main/java/no/ssb/dapla/dlp/pseudo/func/redact/RedactFunc.java +++ b/src/main/java/no/ssb/dapla/dlp/pseudo/func/redact/RedactFunc.java @@ -1,6 +1,5 @@ package no.ssb.dapla.dlp.pseudo.func.redact; -import com.google.common.base.Strings; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import no.ssb.dapla.dlp.pseudo.func.AbstractPseudoFunc; @@ -8,9 +7,6 @@ import no.ssb.dapla.dlp.pseudo.func.PseudoFuncInput; import no.ssb.dapla.dlp.pseudo.func.PseudoFuncOutput; -import static com.google.common.base.Strings.emptyToNull; -import static com.google.common.base.Strings.nullToEmpty; - @Slf4j public class RedactFunc extends AbstractPseudoFunc { private final RedactFuncConfigService configService = new RedactFuncConfigService(); @@ -31,29 +27,19 @@ public RedactFunc(@NonNull PseudoFuncConfig genericConfig) { @Override public PseudoFuncOutput apply(PseudoFuncInput input) { - PseudoFuncOutput output = new PseudoFuncOutput(); - input.getValues().forEach(in -> { - String plain = String.valueOf(in); - if (plain.trim().isEmpty()) { - output.add(plain); - } - else if (config.getRegex() != null) { - output.add(plain.replaceAll(config.getRegex(), config.getPlaceholder())); - } - else { - output.add(config.getPlaceholder()); - } - }); - - return output; + String plain = input.value(); + if (plain.trim().isEmpty()) { + return PseudoFuncOutput.of(plain); + } else if (config.getRegex() != null) { + return PseudoFuncOutput.of(plain.replaceAll(config.getRegex(), config.getPlaceholder())); + } else { + return PseudoFuncOutput.of(config.getPlaceholder()); + } } @Override public PseudoFuncOutput restore(PseudoFuncInput input) { - PseudoFuncOutput output = new PseudoFuncOutput(); - input.getValues().forEach(output::add); - - return output; + return PseudoFuncOutput.of(input.value()); } } diff --git a/src/main/java/no/ssb/dapla/dlp/pseudo/func/tink/daead/TinkDaeadFunc.java b/src/main/java/no/ssb/dapla/dlp/pseudo/func/tink/daead/TinkDaeadFunc.java index 7b23c98..58cf76a 100644 --- a/src/main/java/no/ssb/dapla/dlp/pseudo/func/tink/daead/TinkDaeadFunc.java +++ b/src/main/java/no/ssb/dapla/dlp/pseudo/func/tink/daead/TinkDaeadFunc.java @@ -3,8 +3,11 @@ import com.google.crypto.tink.DeterministicAead; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import no.ssb.dapla.dlp.pseudo.func.*; -import no.ssb.dapla.dlp.pseudo.func.util.FromString; +import no.ssb.dapla.dlp.pseudo.func.AbstractPseudoFunc; +import no.ssb.dapla.dlp.pseudo.func.PseudoFuncConfig; +import no.ssb.dapla.dlp.pseudo.func.PseudoFuncException; +import no.ssb.dapla.dlp.pseudo.func.PseudoFuncInput; +import no.ssb.dapla.dlp.pseudo.func.PseudoFuncOutput; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; @@ -35,36 +38,24 @@ private DeterministicAead daead() { @Override public PseudoFuncOutput apply(PseudoFuncInput input) { - PseudoFuncOutput output = new PseudoFuncOutput(); - input.getValues().forEach(in -> { - String plain = String.valueOf(in); - try { - byte[] ciphertext = daead().encryptDeterministically(plain.getBytes(StandardCharsets.UTF_8), DAEAD_STAMP_BYTES); - output.add(Base64.getEncoder().encodeToString(ciphertext)); - } - catch (GeneralSecurityException e) { - throw new DaeadPseudoFuncException("DAEAD apply error. func=" + getFuncDecl() + ", contentType=" + input.getParamMetadata(), e); - } - }); - - return output; + String plain = input.value(); + try { + byte[] ciphertext = daead().encryptDeterministically(plain.getBytes(StandardCharsets.UTF_8), DAEAD_STAMP_BYTES); + return PseudoFuncOutput.of(Base64.getEncoder().encodeToString(ciphertext)); + } catch (GeneralSecurityException e) { + throw new DaeadPseudoFuncException("DAEAD apply error. func=" + getFuncDecl(), e); + } } @Override public PseudoFuncOutput restore(PseudoFuncInput input) { - PseudoFuncOutput output = new PseudoFuncOutput(); - input.getValues().forEach(in -> { - byte[] ciphertext = Base64.getDecoder().decode(String.valueOf(in)); - try { - byte[] plaintext = daead().decryptDeterministically(ciphertext, DAEAD_STAMP_BYTES); - output.add(FromString.convert(new String(plaintext), in.getClass())); - } - catch (GeneralSecurityException e) { - throw new DaeadPseudoFuncException("DAEAD restore error. func=" + getFuncDecl() + ", contentType=" + input.getParamMetadata(), e); - } - }); - - return output; + byte[] ciphertext = Base64.getDecoder().decode(input.value()); + try { + byte[] plaintext = daead().decryptDeterministically(ciphertext, DAEAD_STAMP_BYTES); + return PseudoFuncOutput.of(new String(plaintext)); + } catch (GeneralSecurityException e) { + throw new DaeadPseudoFuncException("DAEAD restore error. func=" + getFuncDecl(), e); + } } public static class DaeadPseudoFuncException extends PseudoFuncException { diff --git a/src/main/java/no/ssb/dapla/dlp/pseudo/func/tink/fpe/TinkFpeFunc.java b/src/main/java/no/ssb/dapla/dlp/pseudo/func/tink/fpe/TinkFpeFunc.java index b3ef858..0f589d3 100644 --- a/src/main/java/no/ssb/dapla/dlp/pseudo/func/tink/fpe/TinkFpeFunc.java +++ b/src/main/java/no/ssb/dapla/dlp/pseudo/func/tink/fpe/TinkFpeFunc.java @@ -12,7 +12,6 @@ @Slf4j public class TinkFpeFunc extends AbstractPseudoFunc { - private final TinkFpeFuncConfigService configService = new TinkFpeFuncConfigService(); private final TinkFpeFuncConfig config; private static final String ALGORITHM = "TINK-FPE"; @@ -23,6 +22,7 @@ public String getAlgorithm() { public TinkFpeFunc(@NonNull PseudoFuncConfig genericConfig) { super(genericConfig.getFuncDecl()); + TinkFpeFuncConfigService configService = new TinkFpeFuncConfigService(); this.config = configService.resolve(genericConfig); } @@ -32,36 +32,25 @@ private Fpe fpe() { @Override public PseudoFuncOutput apply(PseudoFuncInput input) { - PseudoFuncOutput output = new PseudoFuncOutput(); - input.getValues().forEach(in -> { - String plain = String.valueOf(in); - try { - byte[] ciphertext = fpe().encrypt(s2b(plain), config.getFpeParams()); - output.add(b2s(ciphertext)); - } - catch (GeneralSecurityException e) { - throw new TinkFpePseudoFuncException("Tink FPE apply error. func=" + getFuncDecl() + ", contentType=" + input.getParamMetadata(), e); - } - }); - - return output; + String plain = String.valueOf(input.value()); + try { + byte[] ciphertext = fpe().encrypt(s2b(plain), config.getFpeParams()); + return PseudoFuncOutput.of(b2s(ciphertext)); + } + catch (GeneralSecurityException e) { + throw new TinkFpePseudoFuncException("Tink FPE apply error. func=" + getFuncDecl(), e); + } } @Override public PseudoFuncOutput restore(PseudoFuncInput input) { - PseudoFuncOutput output = new PseudoFuncOutput(); - input.getValues().forEach(in -> { - byte[] ciphertext = s2b(String.valueOf(in)); - try { - byte[] plaintext = fpe().decrypt(ciphertext, config.getFpeParams()); - output.add(b2s(plaintext)); - } - catch (GeneralSecurityException e) { - throw new TinkFpePseudoFuncException("Tink FPE restore error. func=" + getFuncDecl() + ", contentType=" + input.getParamMetadata(), e); - } - }); - - return output; + byte[] ciphertext = s2b(String.valueOf(input.value())); + try { + byte[] plaintext = fpe().decrypt(ciphertext, config.getFpeParams()); + return PseudoFuncOutput.of(b2s(plaintext)); + } catch (GeneralSecurityException e) { + throw new TinkFpePseudoFuncException("Tink FPE restore error. func=" + getFuncDecl(), e); + } } public static class TinkFpePseudoFuncException extends PseudoFuncException { diff --git a/src/test/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncInputTest.java b/src/test/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncInputTest.java deleted file mode 100644 index f1156e4..0000000 --- a/src/test/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncInputTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package no.ssb.dapla.dlp.pseudo.func; - -import com.google.common.collect.ImmutableList; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class PseudoFuncInputTest { - - @Test - void singleObject_newPseudoFuncOutput_shouldPopulateInternalListProperly() { - assertThat(PseudoFuncInput.of("foo").getStringValues()).containsExactly("foo"); - } - - @Test - void listOfObjects_newPseudoFuncOutput_shouldPopulateInternalListProperly() { - assertThat(PseudoFuncInput.of(ImmutableList.of("foo", "bar")).getStringValues()).containsExactly("foo", "bar"); - } - - @Test - void arrayOfObjects_newPseudoFuncOutput_shouldPopulateInternalListProperly() { - assertThat(PseudoFuncInput.of(new String[] {"foo", "bar"}).getStringValues()).containsExactly("foo", "bar"); - } - -} \ No newline at end of file diff --git a/src/test/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncOutputTest.java b/src/test/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncOutputTest.java deleted file mode 100644 index 3dd54b4..0000000 --- a/src/test/java/no/ssb/dapla/dlp/pseudo/func/PseudoFuncOutputTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package no.ssb.dapla.dlp.pseudo.func; - -import com.google.common.collect.ImmutableList; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class PseudoFuncOutputTest { - - @Test - void singleObject_newPseudoFuncOutput_shouldPopulateInternalListProperly() { - assertThat(PseudoFuncOutput.of("foo").getStringValues()).containsExactly("foo"); - } - - @Test - void listOfObjects_newPseudoFuncOutput_shouldPopulateInternalListProperly() { - assertThat(PseudoFuncOutput.of(ImmutableList.of("foo", "bar")).getStringValues()).containsExactly("foo", "bar"); - } - - @Test - void arrayOfObjects_newPseudoFuncOutput_shouldPopulateInternalListProperly() { - assertThat(PseudoFuncOutput.of(new String[] {"foo", "bar"}).getStringValues()).containsExactly("foo", "bar"); - } - -} \ No newline at end of file diff --git a/src/test/java/no/ssb/dapla/dlp/pseudo/func/fpe/FpeFuncTest.java b/src/test/java/no/ssb/dapla/dlp/pseudo/func/fpe/FpeFuncTest.java index 8ddea32..4733fe8 100644 --- a/src/test/java/no/ssb/dapla/dlp/pseudo/func/fpe/FpeFuncTest.java +++ b/src/test/java/no/ssb/dapla/dlp/pseudo/func/fpe/FpeFuncTest.java @@ -1,15 +1,16 @@ package no.ssb.dapla.dlp.pseudo.func.fpe; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import no.ssb.dapla.dlp.pseudo.func.*; +import no.ssb.dapla.dlp.pseudo.func.PseudoFunc; +import no.ssb.dapla.dlp.pseudo.func.PseudoFuncConfig; +import no.ssb.dapla.dlp.pseudo.func.PseudoFuncFactory; +import no.ssb.dapla.dlp.pseudo.func.PseudoFuncInput; +import no.ssb.dapla.dlp.pseudo.func.PseudoFuncOutput; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; class FpeFuncTest { @@ -29,19 +30,6 @@ void alphanumeric_fpe_shouldTransformAndRestore() { ))); } - @Test - void multipleAlphanumeric_fpe_shouldTransformAndRestore() { - List originalVal = ImmutableList.of("Ken sent me...", "Kilroy was here!"); - List expectedVal = ImmutableList.of("dvm.'pvDY¼&lZ$", "p³h