From 7c3c038873d7f5183a3b416976d10fb82b08fd35 Mon Sep 17 00:00:00 2001 From: Piotr Belke Date: Sat, 19 Oct 2024 19:58:02 +0200 Subject: [PATCH] IKC-412 Apply data masking policies to topic messages --- .../datamasking/DataMaskingExcpetion.java | 8 ++ .../datamasking/DataMaskingService.java | 47 ++++++++++ .../kouncil/datamasking/PolicyApplier.java | 90 +++++++++++++++++++ .../kouncil/datamasking/dto/PolicyDto.java | 2 - .../datamasking/dto/PolicyFieldDto.java | 4 +- .../model/datamasking/FieldFindRule.java | 7 -- .../kouncil/model/datamasking/Policy.java | 6 -- .../model/datamasking/PolicyField.java | 4 +- .../consdata/kouncil/topic/TopicService.java | 23 ++--- .../db/migration/V7__data_masking.sql | 11 ++- .../datamasking/PolicyApplierTest.java | 83 +++++++++++++++++ .../converter/PolicyConverterTest.java | 3 - .../converter/PolicyDtoConverterTest.java | 3 - .../input/complex_object_test.json | 13 +++ .../input/object_with_arrays_test.json | 26 ++++++ .../input/simple_object_test.json | 4 + .../output/complex_object_test_result.json | 13 +++ .../object_with_arrays_test_result.json | 26 ++++++ .../output/simple_object_test_result.json | 4 + .../select-field/select-field.component.ts | 3 +- .../src/lib/policies/policies.component.ts | 12 +-- .../feat-data-masking/src/lib/policy.model.ts | 9 +- .../src/lib/policy/policy-form.component.ts | 13 +-- .../policy-form-fields.component.ts | 16 ++-- .../policy-form-resources.component.ts | 2 +- 25 files changed, 352 insertions(+), 80 deletions(-) create mode 100644 kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/DataMaskingExcpetion.java create mode 100644 kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/DataMaskingService.java create mode 100644 kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/PolicyApplier.java delete mode 100644 kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/FieldFindRule.java create mode 100644 kouncil-backend/src/test/java/com/consdata/kouncil/datamasking/PolicyApplierTest.java create mode 100644 kouncil-backend/src/test/resources/data-masking/input/complex_object_test.json create mode 100644 kouncil-backend/src/test/resources/data-masking/input/object_with_arrays_test.json create mode 100644 kouncil-backend/src/test/resources/data-masking/input/simple_object_test.json create mode 100644 kouncil-backend/src/test/resources/data-masking/output/complex_object_test_result.json create mode 100644 kouncil-backend/src/test/resources/data-masking/output/object_with_arrays_test_result.json create mode 100644 kouncil-backend/src/test/resources/data-masking/output/simple_object_test_result.json diff --git a/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/DataMaskingExcpetion.java b/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/DataMaskingExcpetion.java new file mode 100644 index 00000000..ddc905eb --- /dev/null +++ b/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/DataMaskingExcpetion.java @@ -0,0 +1,8 @@ +package com.consdata.kouncil.datamasking; + +public class DataMaskingExcpetion extends RuntimeException { + + public DataMaskingExcpetion(Exception e) { + super(e); + } +} diff --git a/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/DataMaskingService.java b/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/DataMaskingService.java new file mode 100644 index 00000000..fcdc02f7 --- /dev/null +++ b/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/DataMaskingService.java @@ -0,0 +1,47 @@ +package com.consdata.kouncil.datamasking; + +import com.consdata.kouncil.clusters.ClusterRepository; +import com.consdata.kouncil.model.cluster.Cluster; +import com.consdata.kouncil.model.datamasking.Policy; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class DataMaskingService { + + private final PolicyRepository repository; + private final ClusterRepository clusterRepository; + + public String maskTopicMessage(String message, String topic, String clusterId) { + List list = getPoliciesForClusterAndTopic(topic, clusterId); + + for (Policy policy : list) { + message = PolicyApplier.apply(policy, message); + } + return message; + } + + private List getPoliciesForClusterAndTopic(String topic, String clusterId) { + Map clusters = StreamSupport.stream(clusterRepository.findAll().spliterator(), false) + .collect(Collectors.toMap(Cluster::getId, cluster -> cluster)); + Set policies = StreamSupport.stream(repository.findAll().spliterator(), false) + .collect(Collectors.toSet()); + + return policies.stream() + .filter(policy -> policy.getResources().stream().anyMatch(resource -> clusters.get(resource.getCluster()).getName().equals(clusterId))) + .filter(policy -> policy.getResources().stream().anyMatch(resource -> { + Pattern pattern = Pattern.compile(resource.getTopic()); + return pattern.matcher(topic).matches(); + })) + .toList(); + } +} diff --git a/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/PolicyApplier.java b/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/PolicyApplier.java new file mode 100644 index 00000000..c830068b --- /dev/null +++ b/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/PolicyApplier.java @@ -0,0 +1,90 @@ +package com.consdata.kouncil.datamasking; + +import com.consdata.kouncil.model.datamasking.MaskingType; +import com.consdata.kouncil.model.datamasking.Policy; +import com.consdata.kouncil.model.datamasking.PolicyField; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.IOException; +import java.io.StringReader; +import java.util.Arrays; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class PolicyApplier { + + private static final int MASKING_SIGNS_AMOUNT = 5; + private static final String MASKING_SIGN = "*"; + private static final String FIELD_SEPARATOR = "\\."; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + static { + OBJECT_MAPPER.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true); + } + + public static String apply(Policy policy, String value) { + try { + JsonNode jsonNode = OBJECT_MAPPER.readTree(new StringReader(value)); + for (PolicyField field : policy.getFields()) { + String[] split = field.getField().split(FIELD_SEPARATOR); + traversFieldPathAndUpdateValue(field, jsonNode, split, 0); + } + return jsonNode.toString(); + } catch (IOException e) { + throw new DataMaskingExcpetion(e); + } + } + + private static void traversFieldPathAndUpdateValue(PolicyField policyField, JsonNode jsonNode, String[] path, int index) { + String fieldNameFromPath = path[0]; + jsonNode.fieldNames().forEachRemaining(jsonChildFieldName -> processField(policyField, fieldNameFromPath, jsonChildFieldName, jsonNode, path, index)); + } + + private static void processField(PolicyField policyField, String fieldNameFromPath, String jsonChildFieldName, JsonNode jsonNode, String[] path, + int index) { + if (fieldNameFromPath.equals(jsonChildFieldName)) { + JsonNode childNode = jsonNode.get(fieldNameFromPath); + if (childNode.isObject()) { + processObjectNode(policyField, childNode, Arrays.copyOfRange(path, index + 1, path.length), index); + } else { + processValueNode(childNode, policyField, index, path, jsonNode, fieldNameFromPath); + } + } + } + + private static void processObjectNode(PolicyField policyField, JsonNode childNode, String[] path, int index) { + traversFieldPathAndUpdateValue(policyField, childNode, path, index); + } + + private static void processValueNode(JsonNode childNode, PolicyField policyField, int index, String[] path, JsonNode jsonNode, String fieldNameFromPath) { + if (childNode.isArray()) { + for (int i = 0; i < childNode.size(); i++) { + if (childNode.get(i).isValueNode()) { + ((ArrayNode) childNode).set(i, maskFieldValue(policyField.getMaskingType(), childNode.get(i).asText())); + } else { + processObjectNode(policyField, childNode.get(i), Arrays.copyOfRange(path, index + 1, path.length), index); + } + } + } else { + ((ObjectNode) jsonNode).put(fieldNameFromPath, maskFieldValue(policyField.getMaskingType(), childNode.asText())); + } + } + + private static String maskFieldValue(MaskingType policyMaskingType, String fieldValue) { + String newFieldValue = fieldValue; + if (fieldValue.length() < MASKING_SIGNS_AMOUNT || MaskingType.ALL.equals(policyMaskingType)) { + newFieldValue = MASKING_SIGN.repeat(fieldValue.length()); + } else if (MaskingType.FIRST_5.equals(policyMaskingType)) { + newFieldValue = MASKING_SIGN.repeat(MASKING_SIGNS_AMOUNT) + fieldValue.substring(MASKING_SIGNS_AMOUNT); + } else if (MaskingType.LAST_5.equals(policyMaskingType)) { + newFieldValue = fieldValue.substring(0, fieldValue.length() - MASKING_SIGNS_AMOUNT) + MASKING_SIGN.repeat(MASKING_SIGNS_AMOUNT); + } + return newFieldValue; + } +} diff --git a/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/dto/PolicyDto.java b/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/dto/PolicyDto.java index 35b4e506..09c2f7e8 100644 --- a/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/dto/PolicyDto.java +++ b/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/dto/PolicyDto.java @@ -1,6 +1,5 @@ package com.consdata.kouncil.datamasking.dto; -import com.consdata.kouncil.model.datamasking.MaskingType; import java.util.Set; import lombok.Data; @@ -9,7 +8,6 @@ public class PolicyDto { private Long id; private String name; - private MaskingType maskingType; private Boolean applyToAllResources; private Set fields; private Set resources; diff --git a/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/dto/PolicyFieldDto.java b/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/dto/PolicyFieldDto.java index cc7d6487..7777a39c 100644 --- a/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/dto/PolicyFieldDto.java +++ b/kouncil-backend/src/main/java/com/consdata/kouncil/datamasking/dto/PolicyFieldDto.java @@ -1,12 +1,12 @@ package com.consdata.kouncil.datamasking.dto; -import com.consdata.kouncil.model.datamasking.FieldFindRule; +import com.consdata.kouncil.model.datamasking.MaskingType; import lombok.Data; @Data public class PolicyFieldDto { private Long id; - private FieldFindRule findRule; + private MaskingType maskingType; private String field; } diff --git a/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/FieldFindRule.java b/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/FieldFindRule.java deleted file mode 100644 index bbe29035..00000000 --- a/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/FieldFindRule.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.consdata.kouncil.model.datamasking; - -public enum FieldFindRule { - - ANY_LEVEL, - EXACT_PATH -} diff --git a/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/Policy.java b/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/Policy.java index df3aefeb..474e2e38 100644 --- a/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/Policy.java +++ b/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/Policy.java @@ -4,8 +4,6 @@ import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -32,10 +30,6 @@ public class Policy { @Column(name = "NAME") private String name; - @Column(name = "MASKING_TYPE") - @Enumerated(EnumType.STRING) - private MaskingType maskingType; - @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER, orphanRemoval = true) @JoinColumn(name = "POLICY_ID") private Set fields; diff --git a/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/PolicyField.java b/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/PolicyField.java index 67378b08..f2b3f6af 100644 --- a/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/PolicyField.java +++ b/kouncil-backend/src/main/java/com/consdata/kouncil/model/datamasking/PolicyField.java @@ -30,9 +30,9 @@ public class PolicyField { @Column(name = "FIELD") private String field; - @Column(name = "FIND_RULE") + @Column(name = "MASKING_TYPE") @Enumerated(EnumType.STRING) - private FieldFindRule findRule; + private MaskingType maskingType; @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinColumn(name = "POLICY_ID", insertable = false, updatable = false) diff --git a/kouncil-backend/src/main/java/com/consdata/kouncil/topic/TopicService.java b/kouncil-backend/src/main/java/com/consdata/kouncil/topic/TopicService.java index 06edbd38..68874172 100644 --- a/kouncil-backend/src/main/java/com/consdata/kouncil/topic/TopicService.java +++ b/kouncil-backend/src/main/java/com/consdata/kouncil/topic/TopicService.java @@ -6,6 +6,7 @@ import com.consdata.kouncil.KafkaConnectionService; import com.consdata.kouncil.KouncilRuntimeException; import com.consdata.kouncil.MessagesHelper; +import com.consdata.kouncil.datamasking.DataMaskingService; import com.consdata.kouncil.serde.deserialization.DeserializationService; import com.consdata.kouncil.serde.deserialization.DeserializedMessage; import com.consdata.kouncil.serde.serialization.SerializationService; @@ -58,6 +59,7 @@ public class TopicService { private final KafkaConnectionService kafkaConnectionService; private final SerializationService serializationService; private final DeserializationService deserializationService; + private final DataMaskingService dataMaskingService; private final MessagesHelper messagesHelper; private static final int RESEND_MAX_POLL_RECORDS = 100; @@ -65,13 +67,13 @@ public class TopicService { private String[] resendHeadersToKeep; TopicMessagesDto getTopicMessages(@PathVariable("topicName") String topicName, - @PathVariable("partition") String partitions, - @RequestParam("page") String pageParam, - @RequestParam("limit") String limitParam, - @RequestParam(value = "beginningTimestampMillis", required = false) Long beginningTimestampMillis, - @RequestParam(value = "endTimestampMillis", required = false) Long endTimestampMillis, - @RequestParam(value = "offset", required = false) Long offset, - @RequestParam("serverId") String serverId) { + @PathVariable("partition") String partitions, + @RequestParam("page") String pageParam, + @RequestParam("limit") String limitParam, + @RequestParam(value = "beginningTimestampMillis", required = false) Long beginningTimestampMillis, + @RequestParam(value = "endTimestampMillis", required = false) Long endTimestampMillis, + @RequestParam(value = "offset", required = false) Long offset, + @RequestParam("serverId") String serverId) { messagesHelper.validateTopics(serverId, singletonList(topicName)); int limit = Integer.parseInt(limitParam); // per partition! long page = Long.parseLong(pageParam); // per partition! @@ -191,12 +193,13 @@ private void pollMessages(String clusterId, int limit, KafkaConsumer()); + policy.getFields().add(createField(MaskingType.LAST_5, "firstName")); + policy.getFields().add(createField(MaskingType.FIRST_5, "lastName")); + String apply = PolicyApplier.apply(policy, message); + + + String result = objectMapper.readTree(new StringReader(loadFile("data-masking/output/simple_object_test_result.json"))).toString(); + assertThat(apply).isEqualTo(result); + } + + @Test + void shouldApplyPoliciesToComplexMessage() throws URISyntaxException, IOException { + String message = loadFile("data-masking/input/complex_object_test.json"); + + Policy policy = new Policy(); + policy.setFields(new HashSet<>()); + policy.getFields().add(createField(MaskingType.LAST_5, "firstName")); + policy.getFields().add(createField(MaskingType.FIRST_5, "lastName")); + policy.getFields().add(createField(MaskingType.ALL, "address.street")); + policy.getFields().add(createField(MaskingType.FIRST_5, "longestTravels")); + String apply = PolicyApplier.apply(policy, message); + + + String result = objectMapper.readTree(new StringReader(loadFile("data-masking/output/complex_object_test_result.json"))).toString(); + assertThat(apply).isEqualTo(result); + } + + @Test + void shouldApplyPoliciesToMessageWithArrays() throws URISyntaxException, IOException { + String message = loadFile("data-masking/input/object_with_arrays_test.json"); + + Policy policy = new Policy(); + policy.setFields(new HashSet<>()); + policy.getFields().add(createField(MaskingType.ALL, "firstName")); + policy.getFields().add(createField(MaskingType.FIRST_5, "lastName")); + policy.getFields().add(createField(MaskingType.FIRST_5, "salary")); + policy.getFields().add(createField(MaskingType.FIRST_5, "address.street")); + policy.getFields().add(createField(MaskingType.LAST_5, "address.street")); + policy.getFields().add(createField(MaskingType.LAST_5, "address.postalCodes")); + policy.getFields().add(createField(MaskingType.ALL, "hobbies")); + String apply = PolicyApplier.apply(policy, message); + + String result = objectMapper.readTree(new StringReader(loadFile("data-masking/output/object_with_arrays_test_result.json"))).toString(); + assertThat(apply).isEqualTo(result); + } + + private String loadFile(String filePath) throws URISyntaxException, IOException { + return Files.readString(Paths.get(Objects.requireNonNull(PolicyApplierTest.class.getClassLoader().getResource(filePath)).toURI())); + } + + private PolicyField createField(MaskingType maskingType, String field) { + PolicyField policyField = new PolicyField(); + policyField.setMaskingType(maskingType); + policyField.setField(field); + return policyField; + } +} diff --git a/kouncil-backend/src/test/java/com/consdata/kouncil/datamasking/converter/PolicyConverterTest.java b/kouncil-backend/src/test/java/com/consdata/kouncil/datamasking/converter/PolicyConverterTest.java index c170af08..741e0717 100644 --- a/kouncil-backend/src/test/java/com/consdata/kouncil/datamasking/converter/PolicyConverterTest.java +++ b/kouncil-backend/src/test/java/com/consdata/kouncil/datamasking/converter/PolicyConverterTest.java @@ -6,7 +6,6 @@ import com.consdata.kouncil.datamasking.dto.PolicyDto; import com.consdata.kouncil.datamasking.dto.PolicyFieldDto; import com.consdata.kouncil.datamasking.dto.PolicyResourceDto; -import com.consdata.kouncil.model.datamasking.MaskingType; import com.consdata.kouncil.model.datamasking.Policy; import java.util.HashSet; import org.junit.jupiter.api.Test; @@ -20,7 +19,6 @@ void should_convert_to_entity() { policyDto.setId(1L); policyDto.setName("test"); policyDto.setApplyToAllResources(false); - policyDto.setMaskingType(MaskingType.ALL); policyDto.setFields(new HashSet<>()); policyDto.getFields().add(createField(1L)); policyDto.getFields().add(createField(2L)); @@ -35,7 +33,6 @@ void should_convert_to_entity() { () -> assertThat(policy.getId()).isEqualTo(policyDto.getId()), () -> assertThat(policy.getName()).isEqualTo(policyDto.getName()), () -> assertThat(policy.getApplyToAllResources()).isEqualTo(policyDto.getApplyToAllResources()), - () -> assertThat(policy.getMaskingType()).isEqualTo(policyDto.getMaskingType()), () -> assertThat(policy.getFields()).hasSize(policyDto.getFields().size()), () -> assertThat(policy.getResources()).hasSize(policyDto.getResources().size()) ); diff --git a/kouncil-backend/src/test/java/com/consdata/kouncil/datamasking/converter/PolicyDtoConverterTest.java b/kouncil-backend/src/test/java/com/consdata/kouncil/datamasking/converter/PolicyDtoConverterTest.java index fa63f39d..7c89828f 100644 --- a/kouncil-backend/src/test/java/com/consdata/kouncil/datamasking/converter/PolicyDtoConverterTest.java +++ b/kouncil-backend/src/test/java/com/consdata/kouncil/datamasking/converter/PolicyDtoConverterTest.java @@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertAll; import com.consdata.kouncil.datamasking.dto.PolicyDto; -import com.consdata.kouncil.model.datamasking.MaskingType; import com.consdata.kouncil.model.datamasking.Policy; import com.consdata.kouncil.model.datamasking.PolicyField; import com.consdata.kouncil.model.datamasking.PolicyResource; @@ -20,7 +19,6 @@ void should_convert_to_dto() { policy.setId(1L); policy.setName("test"); policy.setApplyToAllResources(false); - policy.setMaskingType(MaskingType.ALL); policy.setFields(new HashSet<>()); policy.getFields().add(createField(1L)); policy.getFields().add(createField(2L)); @@ -35,7 +33,6 @@ void should_convert_to_dto() { () -> assertThat(policyDto.getId()).isEqualTo(policy.getId()), () -> assertThat(policyDto.getName()).isEqualTo(policy.getName()), () -> assertThat(policyDto.getApplyToAllResources()).isEqualTo(policy.getApplyToAllResources()), - () -> assertThat(policyDto.getMaskingType()).isEqualTo(policy.getMaskingType()), () -> assertThat(policyDto.getFields()).hasSize(policy.getFields().size()), () -> assertThat(policyDto.getResources()).hasSize(policy.getResources().size()) ); diff --git a/kouncil-backend/src/test/resources/data-masking/input/complex_object_test.json b/kouncil-backend/src/test/resources/data-masking/input/complex_object_test.json new file mode 100644 index 00000000..fb781db8 --- /dev/null +++ b/kouncil-backend/src/test/resources/data-masking/input/complex_object_test.json @@ -0,0 +1,13 @@ +{ + "firstName": "Jonathan", + "lastName": "Holloway", + "address": { + "street": "7361 Roehampton Ave.", + "streetNumber": 15 + }, + "longestTravels": [ + 12345.1234, + 6547.123, + 98712345.98989 + ] +} diff --git a/kouncil-backend/src/test/resources/data-masking/input/object_with_arrays_test.json b/kouncil-backend/src/test/resources/data-masking/input/object_with_arrays_test.json new file mode 100644 index 00000000..ccc712e3 --- /dev/null +++ b/kouncil-backend/src/test/resources/data-masking/input/object_with_arrays_test.json @@ -0,0 +1,26 @@ +{ + "firstName": "Jonathan", + "lastName": "Holloway", + "address": [ + { + "street": "7361 Roehampton Ave.", + "streetNumber": 15 + }, + { + "street": "77 Laurel St.", + "streetNumber": 22, + "postalCodes": [ + 88978, + 88979, + 88980 + ] + } + ], + "hobbies": [ + "cooking", + "running", + "programming", + "pets" + ], + "salary": 123456.7 +} diff --git a/kouncil-backend/src/test/resources/data-masking/input/simple_object_test.json b/kouncil-backend/src/test/resources/data-masking/input/simple_object_test.json new file mode 100644 index 00000000..a19756f6 --- /dev/null +++ b/kouncil-backend/src/test/resources/data-masking/input/simple_object_test.json @@ -0,0 +1,4 @@ +{ + "firstName": "Jonathan", + "lastName": "Holloway" +} diff --git a/kouncil-backend/src/test/resources/data-masking/output/complex_object_test_result.json b/kouncil-backend/src/test/resources/data-masking/output/complex_object_test_result.json new file mode 100644 index 00000000..8eb68b57 --- /dev/null +++ b/kouncil-backend/src/test/resources/data-masking/output/complex_object_test_result.json @@ -0,0 +1,13 @@ +{ + "firstName": "Jon*****", + "lastName": "*****way", + "address": { + "street": "********************", + "streetNumber": 15 + }, + "longestTravels": [ + "*****.1234", + "*****123", + "*****345.98989" + ] +} diff --git a/kouncil-backend/src/test/resources/data-masking/output/object_with_arrays_test_result.json b/kouncil-backend/src/test/resources/data-masking/output/object_with_arrays_test_result.json new file mode 100644 index 00000000..444704d2 --- /dev/null +++ b/kouncil-backend/src/test/resources/data-masking/output/object_with_arrays_test_result.json @@ -0,0 +1,26 @@ +{ + "firstName": "********", + "lastName": "*****way", + "address": [ + { + "street": "*****Roehampton*****", + "streetNumber": 15 + }, + { + "street": "*****ure*****", + "streetNumber": 22, + "postalCodes": [ + "*****", + "*****", + "*****" + ] + } + ], + "hobbies": [ + "*******", + "*******", + "***********", + "****" + ], + "salary": "*****6.7" +} diff --git a/kouncil-backend/src/test/resources/data-masking/output/simple_object_test_result.json b/kouncil-backend/src/test/resources/data-masking/output/simple_object_test_result.json new file mode 100644 index 00000000..f5953085 --- /dev/null +++ b/kouncil-backend/src/test/resources/data-masking/output/simple_object_test_result.json @@ -0,0 +1,4 @@ +{ + "firstName": "Jon*****", + "lastName": "*****way" +} diff --git a/kouncil-frontend/libs/common-components/src/lib/select-field/select-field.component.ts b/kouncil-frontend/libs/common-components/src/lib/select-field/select-field.component.ts index 806b9db1..f55a743d 100644 --- a/kouncil-frontend/libs/common-components/src/lib/select-field/select-field.component.ts +++ b/kouncil-frontend/libs/common-components/src/lib/select-field/select-field.component.ts @@ -12,7 +12,7 @@ import {MatSelectChange} from '@angular/material/select'; * - {{ option.label }} @@ -42,6 +42,7 @@ export class SelectFieldComponent { @Input() form: FormGroup; @Input() controlName: string; @Input() label: string; + @Input() placeholder: string; @Input() required: boolean = false; @Input() readonly: boolean = false; @Input() clearValueBtn: boolean = false; diff --git a/kouncil-frontend/libs/feat-data-masking/src/lib/policies/policies.component.ts b/kouncil-frontend/libs/feat-data-masking/src/lib/policies/policies.component.ts index 962f4e25..58056c5f 100644 --- a/kouncil-frontend/libs/feat-data-masking/src/lib/policies/policies.component.ts +++ b/kouncil-frontend/libs/feat-data-masking/src/lib/policies/policies.component.ts @@ -10,7 +10,7 @@ import { SnackBarComponent, SnackBarData } from '@app/common-utils'; -import {MaskingType, Policy, PolicyField} from '../policy.model'; +import {Policy, PolicyField} from '../policy.model'; import {ConfirmService} from '@app/feat-confirm'; import {MatSnackBar} from '@angular/material/snack-bar'; import {PolicyService} from '../policy/policy.service'; @@ -86,16 +86,6 @@ export class PoliciesComponent extends AbstractTableComponent implements OnInit, draggable: true, width: 300 }, - { - name: 'Masking type', - prop: 'maskingType', - sticky: false, - resizeable: true, - sortable: true, - draggable: true, - width: 300, - valueFormatter: (value: MaskingType): string => MaskingType[value] - }, { name: 'Fields', prop: 'fields', diff --git a/kouncil-frontend/libs/feat-data-masking/src/lib/policy.model.ts b/kouncil-frontend/libs/feat-data-masking/src/lib/policy.model.ts index aeaaf314..c264a6bc 100644 --- a/kouncil-frontend/libs/feat-data-masking/src/lib/policy.model.ts +++ b/kouncil-frontend/libs/feat-data-masking/src/lib/policy.model.ts @@ -1,6 +1,6 @@ export class Policy { - constructor(public id: number, public name: string, public maskingType: MaskingType, + constructor(public id: number, public name: string, public applyToAllResources: boolean, public fields: Array, public resources: Array) { } @@ -13,7 +13,7 @@ export class PolicyResource { export class PolicyField { - constructor(public id: number, public findRule: FindRule, public field: string) { + constructor(public id: number, public maskingType: MaskingType, public field: string) { } } @@ -23,8 +23,3 @@ export enum MaskingType { FIRST_5 = 'Hide first 5 signs', LAST_5 = 'Hide last 5 signs' } - -export enum FindRule { - ANY_LEVEL = 'Find at any level', - EXACT_PATH = 'Find field at this level' -} diff --git a/kouncil-frontend/libs/feat-data-masking/src/lib/policy/policy-form.component.ts b/kouncil-frontend/libs/feat-data-masking/src/lib/policy/policy-form.component.ts index d7221c32..c14d5ffb 100644 --- a/kouncil-frontend/libs/feat-data-masking/src/lib/policy/policy-form.component.ts +++ b/kouncil-frontend/libs/feat-data-masking/src/lib/policy/policy-form.component.ts @@ -2,7 +2,7 @@ import {Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {Subscription} from 'rxjs'; import {ViewMode} from '@app/common-utils'; -import {MaskingType, Policy, PolicyField, PolicyResource} from '../policy.model'; +import {Policy, PolicyField, PolicyResource} from '../policy.model'; import { FormArray, FormControl, @@ -36,11 +36,6 @@ import {Clusters, ClustersService} from '@app/feat-clusters'; [form]="policyForm" [controlName]="'name'"> - - @@ -68,7 +63,6 @@ export class PolicyFormComponent implements OnInit, OnDestroy { policyForm: FormGroup = new FormGroup({ id: new FormControl(), name: new FormControl('', [Validators.required]), - maskingType: new FormControl('', [Validators.required]), applyToAllResources: new FormControl(false), fields: new FormArray([], { validators: this.validateFields(), @@ -80,8 +74,6 @@ export class PolicyFormComponent implements OnInit, OnDestroy { }) }); - maskingTypeOptions: Array = Object.keys(MaskingType) - .map(maskingType => new SelectableItem(MaskingType[maskingType], maskingType, false)); clusters: Array = []; @@ -96,7 +88,6 @@ export class PolicyFormComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.subscriptions.add(this.clustersService.getClusters$() .pipe(first()) .subscribe((data: Clusters) => { @@ -156,7 +147,7 @@ export class PolicyFormComponent implements OnInit, OnDestroy { private validateFields(): ValidatorFn { return (): ValidationErrors | null => { const fields: Array = this.policyForm?.get('fields')?.value; - return fields && (fields.length === 0 || fields.some(field => !field.field || !field.findRule)) ? {required: true} : null; + return fields && (fields.length === 0 || fields.some(field => !field.field || !field.maskingType)) ? {required: true} : null; }; } diff --git a/kouncil-frontend/libs/feat-data-masking/src/lib/policy/sections/policy-form-fields/policy-form-fields.component.ts b/kouncil-frontend/libs/feat-data-masking/src/lib/policy/sections/policy-form-fields/policy-form-fields.component.ts index 8b647166..5687a450 100644 --- a/kouncil-frontend/libs/feat-data-masking/src/lib/policy/sections/policy-form-fields/policy-form-fields.component.ts +++ b/kouncil-frontend/libs/feat-data-masking/src/lib/policy/sections/policy-form-fields/policy-form-fields.component.ts @@ -1,6 +1,6 @@ import {AfterViewInit, Component, Input} from '@angular/core'; import {ViewMode} from '@app/common-utils'; -import {FindRule} from '../../../policy.model'; +import {MaskingType} from '../../../policy.model'; import {FormArray, FormControl, FormGroup} from '@angular/forms'; import {SelectableItem} from '@app/common-components'; @@ -28,10 +28,10 @@ import {SelectableItem} from '@app/common-components'; [placeholder]="'Regex or full field name. Use dot (.) as field separator if need path to access your field.'"> - - +