From e5541e2c26a9094a1211de2ea1d2d7f65dbc88c2 Mon Sep 17 00:00:00 2001 From: Agoston Horvath Date: Wed, 16 Mar 2022 10:28:50 +0100 Subject: [PATCH] cleanup. bump versions. --- pom.xml | 12 +-- .../bol/config/EncryptAutoConfiguration.java | 1 - .../com/bol/reflection/ReflectionCache.java | 53 +++++------ .../secure/CachedEncryptionEventListener.java | 6 +- .../bol/secure/FieldEncryptedPredicate.java | 2 +- .../ReflectionEncryptionEventListener.java | 14 +-- src/test/java/com/bol/system/CryptAssert.java | 60 ++++++------ .../cached/CachedEncryptSystemTest.java | 4 - .../FieldDetectionEncryptSystemTest.java | 93 +++++++++---------- .../FieldDetectionMongoDBConfiguration.java | 16 +--- 10 files changed, 120 insertions(+), 141 deletions(-) diff --git a/pom.xml b/pom.xml index e1ba596..2ce5ba9 100644 --- a/pom.xml +++ b/pom.xml @@ -48,19 +48,19 @@ org.mongodb mongodb-driver-sync - 4.1.1 + 4.5.0 provided org.springframework.data spring-data-mongodb - 3.1.1 + 3.3.2 provided org.springframework.boot spring-boot-autoconfigure - 2.4.0 + 2.6.4 provided @@ -68,7 +68,7 @@ org.springframework.boot spring-boot-starter-test - 2.4.0 + 2.6.4 test @@ -86,13 +86,13 @@ junit junit - 4.13.1 + 4.13.2 test org.assertj assertj-core - 3.18.1 + 3.22.0 test diff --git a/src/main/java/com/bol/config/EncryptAutoConfiguration.java b/src/main/java/com/bol/config/EncryptAutoConfiguration.java index 806f27c..7ec2681 100644 --- a/src/main/java/com/bol/config/EncryptAutoConfiguration.java +++ b/src/main/java/com/bol/config/EncryptAutoConfiguration.java @@ -6,7 +6,6 @@ import com.bol.secure.CachedEncryptionEventListener; import com.bol.secure.ReflectionEncryptionEventListener; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/src/main/java/com/bol/reflection/ReflectionCache.java b/src/main/java/com/bol/reflection/ReflectionCache.java index 6c0b4ca..b92e102 100644 --- a/src/main/java/com/bol/reflection/ReflectionCache.java +++ b/src/main/java/com/bol/reflection/ReflectionCache.java @@ -12,26 +12,19 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import static com.bol.secure.FieldEncryptedPredicate.ANNOTATION_PRESENT; - public class ReflectionCache { private static final Logger LOG = LoggerFactory.getLogger(ReflectionCache.class); - private final ConcurrentHashMap> reflectionCache = new ConcurrentHashMap<>(); - + private final ConcurrentHashMap, List> reflectionCache = new ConcurrentHashMap<>(); private final FieldEncryptedPredicate fieldEncryptedPredicate; - public ReflectionCache() { - this(ANNOTATION_PRESENT); - } - public ReflectionCache(FieldEncryptedPredicate fieldEncryptedPredicate) { this.fieldEncryptedPredicate = fieldEncryptedPredicate; } // used by CachedEncryptionEventListener to gather metadata of a class and all it fields, recursively. - public List reflectRecursive(Class objectClass) { + public List reflectRecursive(Class objectClass) { List nodes = reflectionCache.get(objectClass); if (nodes != null) return nodes; @@ -41,7 +34,7 @@ public List reflectRecursive(Class objectClass) { } // building is necessary to avoid putting half-processed data in `reflectionCache` (where it would be returned to other threads) - private List buildRecursive(Class objectClass, HashMap> building) { + private List buildRecursive(Class objectClass, HashMap, List> building) { if (isPrimitive(objectClass)) return Collections.emptyList(); List processed = reflectionCache.get(objectClass); @@ -94,12 +87,12 @@ private List buildRecursive(Class objectClass, HashMap> } // used by ReflectionEncryptionEventListener to map a single Document - public List reflectSingle(Class objectClass) { + public List reflectSingle(Class objectClass) { return reflectionCache.computeIfAbsent(objectClass, this::buildSingle); } // FIXME: this is a slimmed down copy-paste of buildRecursive(); find a way to bring Cached and Reflective listener closer together! - private List buildSingle(Class objectClass) { + private List buildSingle(Class objectClass) { if (isPrimitive(objectClass)) return Collections.emptyList(); List nodes = new ArrayList<>(); @@ -137,14 +130,14 @@ private List buildSingle(Class objectClass) { return nodes; } - List processParameterizedTypes(Type type, HashMap> building) { + List processParameterizedTypes(Type type, HashMap, List> building) { if (type instanceof Class) { - List children = buildRecursive((Class) type, building); + List children = buildRecursive((Class) type, building); if (!children.isEmpty()) return Collections.singletonList(new Node(null, children, Node.Type.DOCUMENT)); } else if (type instanceof ParameterizedType) { ParameterizedType subType = (ParameterizedType) type; - Class rawType = (Class) subType.getRawType(); + Class rawType = (Class) subType.getRawType(); if (Collection.class.isAssignableFrom(rawType)) { List children = processParameterizedTypes(subType.getActualTypeArguments()[0], building); @@ -155,7 +148,7 @@ List processParameterizedTypes(Type type, HashMap> build if (!children.isEmpty()) return Collections.singletonList(new Node(null, children, Node.Type.MAP)); } else { - throw new IllegalArgumentException("Unknown reflective raw type class " + rawType.getClass()); + throw new IllegalArgumentException("Unknown reflective raw type class " + rawType); } } else { @@ -190,22 +183,20 @@ static String parseFieldAnnotation(java.lang.reflect.Field field, String fieldNa } // same as ClassUtils.isPrimitiveOrWrapper(), but also includes String - public static boolean isPrimitive(Class clazz) { + public static boolean isPrimitive(Class clazz) { return clazz.isPrimitive() || primitiveClasses.contains(clazz); } - private static Set primitiveClasses = new HashSet<>(); - - static { - primitiveClasses.add(Boolean.class); - primitiveClasses.add(Byte.class); - primitiveClasses.add(Character.class); - primitiveClasses.add(Double.class); - primitiveClasses.add(Float.class); - primitiveClasses.add(Integer.class); - primitiveClasses.add(Long.class); - primitiveClasses.add(Short.class); - primitiveClasses.add(Void.class); - primitiveClasses.add(String.class); - } + private static final Set> primitiveClasses = new HashSet<>(Arrays.asList( + Boolean.class, + Byte.class, + Character.class, + Double.class, + Float.class, + Integer.class, + Long.class, + Short.class, + Void.class, + String.class + )); } diff --git a/src/main/java/com/bol/secure/CachedEncryptionEventListener.java b/src/main/java/com/bol/secure/CachedEncryptionEventListener.java index 5244c31..ff38321 100644 --- a/src/main/java/com/bol/secure/CachedEncryptionEventListener.java +++ b/src/main/java/com/bol/secure/CachedEncryptionEventListener.java @@ -29,7 +29,7 @@ public CachedEncryptionEventListener(CryptVault cryptVault, FieldEncryptedPredic reflectionCache = new ReflectionCache(fieldEncryptedPredicate); } - Node node(Class clazz) { + Node node(Class clazz) { List children = reflectionCache.reflectRecursive(clazz); if (!children.isEmpty()) return new Node("", children, Node.Type.DOCUMENT); return Node.EMPTY; @@ -77,7 +77,7 @@ void cryptFields(Object o, Node node, Function crypt) { break; case LIST: - cryptList((List) o, node, crypt); + cryptList((List) o, node, crypt); break; default: @@ -88,7 +88,7 @@ void cryptFields(Object o, Node node, Function crypt) { } } - void cryptList(List list, Node node, Function crypt) { + void cryptList(List list, Node node, Function crypt) { if (node.type != Node.Type.LIST) throw new IllegalArgumentException("Expected list for " + node.fieldName + ", got " + node.type); Node mapChildren = node.children.get(0); diff --git a/src/main/java/com/bol/secure/FieldEncryptedPredicate.java b/src/main/java/com/bol/secure/FieldEncryptedPredicate.java index c40baa2..45442dc 100644 --- a/src/main/java/com/bol/secure/FieldEncryptedPredicate.java +++ b/src/main/java/com/bol/secure/FieldEncryptedPredicate.java @@ -5,6 +5,6 @@ public interface FieldEncryptedPredicate extends Predicate { - FieldEncryptedPredicate ANNOTATION_PRESENT = field -> field.isAnnotationPresent(Encrypted.class); + FieldEncryptedPredicate ANNOTATION_PRESENT = field -> field.isAnnotationPresent(Encrypted.class); } diff --git a/src/main/java/com/bol/secure/ReflectionEncryptionEventListener.java b/src/main/java/com/bol/secure/ReflectionEncryptionEventListener.java index 1500765..f8ccbf7 100644 --- a/src/main/java/com/bol/secure/ReflectionEncryptionEventListener.java +++ b/src/main/java/com/bol/secure/ReflectionEncryptionEventListener.java @@ -34,10 +34,10 @@ public ReflectionEncryptionEventListener(CryptVault cryptVault) { public ReflectionEncryptionEventListener(CryptVault cryptVault, FieldEncryptedPredicate fieldEncryptedPredicate) { super(cryptVault); - this.reflectionCache = new ReflectionCache(fieldEncryptedPredicate); + reflectionCache = new ReflectionCache(fieldEncryptedPredicate); } - void cryptDocument(Document document, Class clazz, Function crypt) { + void cryptDocument(Document document, Class clazz, Function crypt) { List nodes = reflectionCache.reflectSingle(clazz); for (Map.Entry field : document.entrySet()) { @@ -71,15 +71,15 @@ void diveInto(Object value, Type type, Function crypt) { // java primitive type; ignore if (isPrimitive(value.getClass())) return; - Class reflectiveClass = null; + Class reflectiveClass = null; Type[] typeArguments = null; - if (type instanceof Class) reflectiveClass = (Class) type; + if (type instanceof Class) reflectiveClass = (Class) type; else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; Type rawType = parameterizedType.getRawType(); typeArguments = parameterizedType.getActualTypeArguments(); if (!(rawType instanceof Class)) throw new IllegalArgumentException("Unknown reflective type class " + type); - reflectiveClass = (Class) rawType; + reflectiveClass = (Class) rawType; } else throw new IllegalArgumentException("Unknown reflective type class " + type); if (value instanceof Document) { @@ -87,7 +87,7 @@ else if (type instanceof ParameterizedType) { if (Map.class.isAssignableFrom(reflectiveClass)) { Type subFieldType = typeArguments[1]; - for (Map.Entry entry : ((Map) value).entrySet()) { + for (Map.Entry entry : ((Map) value).entrySet()) { try { diveInto(entry.getValue(), subFieldType, crypt); } catch (FieldCryptException e) { @@ -106,7 +106,7 @@ else if (type instanceof ParameterizedType) { } else if (value instanceof List) { if (Collection.class.isAssignableFrom(reflectiveClass)) { Type subFieldType = typeArguments[0]; - List list = (List) value; + List list = (List) value; for (int i = 0; i < list.size(); i++) { try { diff --git a/src/test/java/com/bol/system/CryptAssert.java b/src/test/java/com/bol/system/CryptAssert.java index beaf7ee..5431d4a 100644 --- a/src/test/java/com/bol/system/CryptAssert.java +++ b/src/test/java/com/bol/system/CryptAssert.java @@ -7,35 +7,35 @@ public class CryptAssert { - private final CryptVault cryptVault; - - public CryptAssert(CryptVault cryptVault) { - this.cryptVault = cryptVault; - } - - /** - * simplistic mongodb BSON serialization lengths: - * - 10 bytes for wrapping BSONObject prefix - * - 1 byte prefix before field name - * - field name (1 byte/char) - * - 1 byte 0-terminator after field name - * - 4 byte prefix before field value - * - field value (1byte/char) - * - 1 byte 0-terminator after field value - * - 2 bytes 0 terminator for wrapping BSONObject - *

- * (e.g. for a single primitive string, 12 extra bytes are added above its own length) - */ - public void assertCryptLength(Object cryptedSecretBinary, int serializedLength) { - assertThat(cryptedSecretBinary).isInstanceOf(Binary.class); - - Object cryptedSecretBytes = ((Binary) cryptedSecretBinary).getData(); - - assertThat(cryptedSecretBytes).isInstanceOf(byte[].class); - byte[] cryptedBytes = (byte[]) cryptedSecretBytes; - - int expectedCryptedLength = cryptVault.expectedCryptedLength(serializedLength); - assertThat(cryptedBytes.length).isEqualTo(expectedCryptedLength); - } + private final CryptVault cryptVault; + + public CryptAssert(CryptVault cryptVault) { + this.cryptVault = cryptVault; + } + + /** + * simplistic mongodb BSON serialization lengths: + * - 10 bytes for wrapping BSONObject prefix + * - 1 byte prefix before field name + * - field name (1 byte/char) + * - 1 byte 0-terminator after field name + * - 4 byte prefix before field value + * - field value (1byte/char) + * - 1 byte 0-terminator after field value + * - 2 bytes 0 terminator for wrapping BSONObject + *

+ * (e.g. for a single primitive string, 12 extra bytes are added above its own length) + */ + public void assertCryptLength(Object cryptedSecretBinary, int serializedLength) { + assertThat(cryptedSecretBinary).isInstanceOf(Binary.class); + + Object cryptedSecretBytes = ((Binary) cryptedSecretBinary).getData(); + + assertThat(cryptedSecretBytes).isInstanceOf(byte[].class); + byte[] cryptedBytes = (byte[]) cryptedSecretBytes; + + int expectedCryptedLength = cryptVault.expectedCryptedLength(serializedLength); + assertThat(cryptedBytes.length).isEqualTo(expectedCryptedLength); + } } diff --git a/src/test/java/com/bol/system/cached/CachedEncryptSystemTest.java b/src/test/java/com/bol/system/cached/CachedEncryptSystemTest.java index c3f7087..2b4f36a 100644 --- a/src/test/java/com/bol/system/cached/CachedEncryptSystemTest.java +++ b/src/test/java/com/bol/system/cached/CachedEncryptSystemTest.java @@ -9,11 +9,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; -import java.util.Arrays; - import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.data.mongodb.core.query.Criteria.where; -import static org.springframework.data.mongodb.core.query.Query.query; @RunWith(SpringRunner.class) @SpringBootTest(classes = {CachedMongoDBConfiguration.class}) diff --git a/src/test/java/com/bol/system/field/FieldDetectionEncryptSystemTest.java b/src/test/java/com/bol/system/field/FieldDetectionEncryptSystemTest.java index 4cf25f0..1df3d5f 100644 --- a/src/test/java/com/bol/system/field/FieldDetectionEncryptSystemTest.java +++ b/src/test/java/com/bol/system/field/FieldDetectionEncryptSystemTest.java @@ -24,51 +24,50 @@ @SpringBootTest(classes = {FieldDetectionMongoDBConfiguration.class}) public class FieldDetectionEncryptSystemTest { - @Autowired protected MongoTemplate mongoTemplate; - @Autowired protected CryptVault cryptVault; - - private CryptAssert cryptAssert; - - @Before - public void cleanDb() { - mongoTemplate.dropCollection(PlainBean.class); - } - - @PostConstruct - void postConstruct() { - cryptAssert = new CryptAssert(cryptVault); - } - - @Test - public void simpleEncryption() { - PlainBean bean = new PlainBean(); - bean.nonSensitiveData = "grass is green"; - bean.sensitiveData = "earth is flat"; - bean.singleSubBean = new PlainBean.PlainSubBean("grass is green", "earth is flat"); - bean.subBeans = Collections.singletonList(new PlainBean.PlainSubBean("grass is green", "earth is flat")); - - mongoTemplate.save(bean); - - PlainBean fromDb = mongoTemplate.findOne(query(where("_id").is(bean.id)), PlainBean.class); - - assertThat(fromDb.nonSensitiveData).isEqualTo(bean.nonSensitiveData); - assertThat(fromDb.sensitiveData).isEqualTo(bean.sensitiveData); - assertThat(fromDb.singleSubBean.sensitiveData).isEqualTo(bean.singleSubBean.sensitiveData); - assertThat(fromDb.singleSubBean.nonSensitiveData).isEqualTo(bean.singleSubBean.nonSensitiveData); - assertThat(fromDb.subBeans.get(0).sensitiveData).isEqualTo(bean.subBeans.get(0).sensitiveData); - assertThat(fromDb.subBeans.get(0).nonSensitiveData).isEqualTo(bean.subBeans.get(0).nonSensitiveData); - - Document fromMongo = mongoTemplate.getCollection(PlainBean.MONGO_PLAINBEAN).find(new Document("_id", new ObjectId(bean.id))).first(); - assertThat(fromMongo.get("nonSensitiveData")).isEqualTo(bean.nonSensitiveData); - cryptAssert.assertCryptLength(fromMongo.get("sensitiveData"), bean.sensitiveData.length() + 12); - - Document subBeanDocument = (Document)fromMongo.get("singleSubBean"); - assertThat(subBeanDocument.get("nonSensitiveData")).isEqualTo(bean.singleSubBean.nonSensitiveData); - cryptAssert.assertCryptLength(subBeanDocument.get("sensitiveData"), bean.singleSubBean.sensitiveData.length() + 12); - - Document nested = fromMongo.getList("subBeans", Document.class).get(0); - assertThat(nested.get("nonSensitiveData")).isEqualTo(bean.subBeans.get(0).nonSensitiveData); - cryptAssert.assertCryptLength(nested.get("sensitiveData"), bean.subBeans.get(0).sensitiveData.length() + 12); - } - + @Autowired protected MongoTemplate mongoTemplate; + @Autowired protected CryptVault cryptVault; + + private CryptAssert cryptAssert; + + @Before + public void cleanDb() { + mongoTemplate.dropCollection(PlainBean.class); + } + + @PostConstruct + void postConstruct() { + cryptAssert = new CryptAssert(cryptVault); + } + + @Test + public void simpleEncryption() { + PlainBean bean = new PlainBean(); + bean.nonSensitiveData = "grass is green"; + bean.sensitiveData = "earth is flat"; + bean.singleSubBean = new PlainBean.PlainSubBean("grass is green", "earth is flat"); + bean.subBeans = Collections.singletonList(new PlainBean.PlainSubBean("grass is green", "earth is flat")); + + mongoTemplate.save(bean); + + PlainBean fromDb = mongoTemplate.findOne(query(where("_id").is(bean.id)), PlainBean.class); + + assertThat(fromDb.nonSensitiveData).isEqualTo(bean.nonSensitiveData); + assertThat(fromDb.sensitiveData).isEqualTo(bean.sensitiveData); + assertThat(fromDb.singleSubBean.sensitiveData).isEqualTo(bean.singleSubBean.sensitiveData); + assertThat(fromDb.singleSubBean.nonSensitiveData).isEqualTo(bean.singleSubBean.nonSensitiveData); + assertThat(fromDb.subBeans.get(0).sensitiveData).isEqualTo(bean.subBeans.get(0).sensitiveData); + assertThat(fromDb.subBeans.get(0).nonSensitiveData).isEqualTo(bean.subBeans.get(0).nonSensitiveData); + + Document fromMongo = mongoTemplate.getCollection(PlainBean.MONGO_PLAINBEAN).find(new Document("_id", new ObjectId(bean.id))).first(); + assertThat(fromMongo.get("nonSensitiveData")).isEqualTo(bean.nonSensitiveData); + cryptAssert.assertCryptLength(fromMongo.get("sensitiveData"), bean.sensitiveData.length() + 12); + + Document subBeanDocument = (Document) fromMongo.get("singleSubBean"); + assertThat(subBeanDocument.get("nonSensitiveData")).isEqualTo(bean.singleSubBean.nonSensitiveData); + cryptAssert.assertCryptLength(subBeanDocument.get("sensitiveData"), bean.singleSubBean.sensitiveData.length() + 12); + + Document nested = fromMongo.getList("subBeans", Document.class).get(0); + assertThat(nested.get("nonSensitiveData")).isEqualTo(bean.subBeans.get(0).nonSensitiveData); + cryptAssert.assertCryptLength(nested.get("sensitiveData"), bean.subBeans.get(0).sensitiveData.length() + 12); + } } diff --git a/src/test/java/com/bol/system/field/FieldDetectionMongoDBConfiguration.java b/src/test/java/com/bol/system/field/FieldDetectionMongoDBConfiguration.java index 95c3531..fdcf877 100644 --- a/src/test/java/com/bol/system/field/FieldDetectionMongoDBConfiguration.java +++ b/src/test/java/com/bol/system/field/FieldDetectionMongoDBConfiguration.java @@ -1,13 +1,11 @@ package com.bol.system.field; import com.bol.crypt.CryptVault; -import com.bol.secure.FieldEncryptedPredicate; import com.bol.secure.ReflectionEncryptionEventListener; import com.bol.system.MongoDBConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -15,17 +13,13 @@ @Configuration public class FieldDetectionMongoDBConfiguration extends MongoDBConfiguration { - private final Set fields = new HashSet<>( - Arrays.asList("PlainBean.sensitiveData", "PlainSubBean.sensitiveData")); + private final Set fields = new HashSet<>(Arrays.asList("PlainBean.sensitiveData", "PlainSubBean.sensitiveData")); @Bean public ReflectionEncryptionEventListener encryptionEventListener(CryptVault cryptVault) { - return new ReflectionEncryptionEventListener(cryptVault, new FieldEncryptedPredicate() { - @Override - public boolean test(Field field) { - String fieldName = field.getDeclaringClass().getSimpleName() + "." + field.getName(); - return fields.contains(fieldName); - } + return new ReflectionEncryptionEventListener(cryptVault, field -> { + String fieldName = field.getDeclaringClass().getSimpleName() + "." + field.getName(); + return fields.contains(fieldName); }); - }; + } }