diff --git a/README.adoc b/README.adoc index b1f13f8e5..933cac2c2 100644 --- a/README.adoc +++ b/README.adoc @@ -200,13 +200,6 @@ The generated documentation is available from `target/site/reference/html/index. popd ---- -=== Intellij Issue with Importing pom.xml - -There is an issue in Intellij that prevents it from importing modules when one of the module -directories has the same name as the project directory. The work-around is to create a new module (any name, any type will suffice). -When Intellij creates the new module, it will also recognize the existing modules. Once the new module is -created, it can be deleted and Intellij will now recognize the existing modules. - The generated documentation is available from `target/site/reference/html/index.html`. == Examples diff --git a/spring-data-couchbase/pom.xml b/spring-data-couchbase/pom.xml index 24703019e..3186819ce 100644 --- a/spring-data-couchbase/pom.xml +++ b/spring-data-couchbase/pom.xml @@ -1,6 +1,5 @@ - + 4.0.0 @@ -218,13 +217,6 @@ test - - com.couchbase.client - couchbase-encryption - 3.1.0 - test - - @@ -311,9 +303,7 @@ target/generated-test-sources - - org.springframework.data.couchbase.repository.support.CouchbaseAnnotationProcessor - + org.springframework.data.couchbase.repository.support.CouchbaseAnnotationProcessor diff --git a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java index 6e380315c..ba37fffd1 100644 --- a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java +++ b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java @@ -18,9 +18,8 @@ import static com.couchbase.client.java.ClusterOptions.clusterOptions; -import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Set; import org.springframework.beans.factory.config.BeanDefinition; @@ -28,7 +27,6 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Role; -import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.data.convert.CustomConversions; import org.springframework.data.couchbase.CouchbaseClientFactory; @@ -37,7 +35,6 @@ import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; -import org.springframework.data.couchbase.core.convert.OtherConverters; import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService; import org.springframework.data.couchbase.core.convert.translation.TranslationService; import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; @@ -152,9 +149,7 @@ public ClusterEnvironment couchbaseClusterEnvironment() { if (!nonShadowedJacksonPresent()) { throw new CouchbaseException("non-shadowed Jackson not present"); } - CryptoManager cryptoManager = cryptoManager(); - builder.jsonSerializer(JacksonJsonSerializer.create(couchbaseObjectMapper(cryptoManager))); - builder.cryptoManager(cryptoManager); + builder.jsonSerializer(JacksonJsonSerializer.create(couchbaseObjectMapper())); configureEnvironment(builder); return builder.build(); } @@ -285,8 +280,8 @@ public MappingCouchbaseConverter mappingCouchbaseConverter(CouchbaseMappingConte @Bean public TranslationService couchbaseTranslationService() { final JacksonTranslationService jacksonTranslationService = new JacksonTranslationService(); - jacksonTranslationService.setObjectMapper(couchbaseObjectMapper(cryptoManager())); jacksonTranslationService.afterPropertiesSet(); + // for sdk3, we need to ask the mapper _it_ uses to ignore extra fields... JacksonTransformers.MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return jacksonTranslationService; @@ -313,25 +308,10 @@ public CouchbaseMappingContext couchbaseMappingContext(CustomConversions customC */ public ObjectMapper couchbaseObjectMapper() { - return couchbaseObjectMapper(cryptoManager()); - } - - /** - * Creates a {@link ObjectMapper} for the jsonSerializer of the ClusterEnvironment - * - * @param cryptoManager - * @return ObjectMapper - */ - - ObjectMapper mapper; - - public ObjectMapper couchbaseObjectMapper(CryptoManager cryptoManager) { - if (mapper != null) { - return mapper; - } - mapper = new ObjectMapper(); // or use the one from the Java SDK (?) JacksonTransformers.MAPPER + ObjectMapper mapper = new ObjectMapper(); mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.registerModule(new JsonValueModule()); + CryptoManager cryptoManager = null; if (cryptoManager != null) { mapper.registerModule(new EncryptionModule(cryptoManager)); } @@ -340,7 +320,7 @@ public ObjectMapper couchbaseObjectMapper(CryptoManager cryptoManager) { /** * The default blocking transaction manager. It is an implementation of CallbackPreferringTransactionManager - * CallbackPreferringTransactionManagers do not play well with test-cases that rely + * CallbackPreferrringTransactionmanagers do not play well with test-cases that rely * on @TestTransaction/@BeforeTransaction/@AfterTransaction * * @param clientFactory @@ -361,7 +341,6 @@ CouchbaseCallbackTransactionManager couchbaseTransactionManager(CouchbaseClientF TransactionTemplate couchbaseTransactionTemplate(CouchbaseCallbackTransactionManager couchbaseTransactionManager) { return new TransactionTemplate(couchbaseTransactionManager); } - /** * The default TransactionalOperator. * @@ -400,28 +379,11 @@ protected boolean autoIndexCreation() { * and {@link #couchbaseMappingContext(CustomConversions)}. Returns an empty {@link CustomConversions} instance by * default. * - * @param cryptoManagerOptional optional cryptoManager. Make varargs for backwards compatibility. * @return must not be {@literal null}. */ @Bean(name = BeanNames.COUCHBASE_CUSTOM_CONVERSIONS) - public CustomConversions customConversions(CryptoManager... cryptoManagerOptional) { - assert (cryptoManagerOptional == null || cryptoManagerOptional.length <= 1); - CryptoManager cryptoManager = cryptoManagerOptional != null && cryptoManagerOptional.length == 1 - ? cryptoManagerOptional[0] - : null; - List newConverters = new ArrayList(); - // the cryptoConverters take an argument, so they cannot be created in the - // static block of CouchbaseCustomConversions. And they must be set before the super() constructor - // in CouchbaseCustomerConversions - if (cryptoManager != null) { - newConverters.addAll(OtherConverters.getCryptoConverters(cryptoManager)); - } - return new CouchbaseCustomConversions(newConverters); - } - - @Bean - protected CryptoManager cryptoManager() { - return null; + public CustomConversions customConversions() { + return new CouchbaseCustomConversions(Collections.emptyList()); } /** diff --git a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java index 91b817bc6..b5231ef19 100644 --- a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java +++ b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java @@ -20,11 +20,8 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.data.convert.CustomConversions; -import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; -import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.mapping.model.EntityInstantiators; /** @@ -32,7 +29,6 @@ * * @author Michael Nitschinger * @author Mark Paluch - * @author Michael Reiche */ public abstract class AbstractCouchbaseConverter implements CouchbaseConverter, InitializingBean { @@ -97,36 +93,6 @@ public void afterPropertiesSet() { conversions.registerConvertersIn(conversionService); } - /** - * This convertForWriteIfNeeded takes a property and accessor so that the annotations can be accessed (ie. @Encrypted) - * - * @param prop the property to be converted to the class that would actually be stored. - * @param accessor the property accessor - * @return - */ - @Override - public Object convertForWriteIfNeeded(CouchbasePersistentProperty prop, ConvertingPropertyAccessor accessor) { - Object value = accessor.getProperty(prop, prop.getType()); - if (value == null) { - return null; - } - - Object result = this.conversions.getCustomWriteTarget(prop.getType()) // - .map(it -> this.conversionService.convert(value, new TypeDescriptor(prop.getField()), - TypeDescriptor.valueOf(it))) // - .orElseGet(() -> Enum.class.isAssignableFrom(value.getClass()) ? ((Enum) value).name() : value); - - return result; - - } - - /** - * This convertForWriteIfNeed takes only the value to convert. It cannot access the annotations of the Field being - * converted. - * - * @param value the value to be converted to the class that would actually be stored. - * @return - */ @Override public Object convertForWriteIfNeeded(Object value) { if (value == null) { diff --git a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseConverter.java b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseConverter.java index 968916980..23e7b20a3 100644 --- a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseConverter.java +++ b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseConverter.java @@ -22,7 +22,6 @@ import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; import org.springframework.data.mapping.Alias; -import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.util.TypeInformation; /** @@ -38,24 +37,13 @@ public interface CouchbaseConverter /** * Convert the value if necessary to the class that would actually be stored, or leave it as is if no conversion - * needed. This method cannot access the annotations of the field. + * needed. * * @param value the value to be converted to the class that would actually be stored. * @return the converted value (or the same value if no conversion necessary). */ Object convertForWriteIfNeeded(Object value); - /** - * Convert the value if necessary to the class that would actually be stored, or leave it as is if no conversion - * needed. This method can access the annotations of the field. - * - * @param source the property to be converted to the class that would actually be stored. - * @param accessor the property accessor - * @return the converted value (or the same value if no conversion necessary). - */ - Object convertForWriteIfNeeded(final CouchbasePersistentProperty source, - final ConvertingPropertyAccessor accessor); - /** * Return the Class that would actually be stored for a given Class. * diff --git a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java index 5806a0e9c..ba265d4e4 100644 --- a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java +++ b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java @@ -22,8 +22,6 @@ import org.springframework.data.mapping.model.SimpleTypeHolder; -import com.couchbase.client.core.encryption.CryptoManager; - /** * Value object to capture custom conversion. *

@@ -34,7 +32,6 @@ * @author Oliver Gierke * @author Mark Paluch * @author Subhashni Balakrishnan - * @Michael Reiche * @see org.springframework.data.convert.CustomConversions * @see SimpleTypeHolder * @since 2.0 @@ -45,18 +42,6 @@ public class CouchbaseCustomConversions extends org.springframework.data.convert private static final List STORE_CONVERTERS; - private CryptoManager cryptoManager; - - /** - * Expose the CryptoManager used by a DecryptingReadingConverter or EncryptingWritingConverter, if any. There can only - * be one. MappingCouchbaseConverter needs it. - * - * @return cryptoManager - */ - public CryptoManager getCryptoManager() { - return cryptoManager; - } - static { List converters = new ArrayList<>(); @@ -76,31 +61,5 @@ public CryptoManager getCryptoManager() { */ public CouchbaseCustomConversions(final List converters) { super(STORE_CONVERSIONS, converters); - for (Object c : converters) { - if (c instanceof DecryptingReadingConverter) { - CryptoManager foundCryptoManager = ((DecryptingReadingConverter) c).cryptoManager; - if (foundCryptoManager == null) { - throw new RuntimeException(("DecryptingReadingConverter must have a cryptoManager")); - } else { - if (cryptoManager != null && this.cryptoManager != cryptoManager) { - throw new RuntimeException( - "all DecryptingReadingConverters and EncryptingWringConverters must use " + " a single CryptoManager"); - } - } - cryptoManager = foundCryptoManager; - } - if (c instanceof EncryptingWritingConverter) { - CryptoManager foundCryptoManager = ((EncryptingWritingConverter) c).cryptoManager; - if (foundCryptoManager == null) { - throw new RuntimeException(("EncryptingWritingConverter must have a cryptoManager")); - } else { - if (cryptoManager != null && this.cryptoManager != cryptoManager) { - throw new RuntimeException( - "all DecryptingReadingConverters and EncryptingWringConverters must use " + " a single CryptoManager"); - } - } - cryptoManager = foundCryptoManager; - } - } } } diff --git a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/DecryptingReadingConverter.java b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/DecryptingReadingConverter.java deleted file mode 100644 index e7745cd17..000000000 --- a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/DecryptingReadingConverter.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2022 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.core.convert; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.util.HashSet; -import java.util.Set; - -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.ConditionalGenericConverter; -import org.springframework.data.convert.ReadingConverter; -import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; - -import com.couchbase.client.core.encryption.CryptoManager; -import com.couchbase.client.java.encryption.annotation.Encrypted; - -/** - * Use the cryptoManager to decrypt a field - * - * @author Michael Reiche - */ -@ReadingConverter -public class DecryptingReadingConverter implements ConditionalGenericConverter { - - CryptoManager cryptoManager; - ConversionService conversionService; - - public DecryptingReadingConverter(CryptoManager cryptoManager) { - this.cryptoManager = cryptoManager; - } - - @Override - public Set getConvertibleTypes() { - Set convertiblePairs = new HashSet<>(); - Class[] clazzes = new Class[] { String.class, Integer.class, Long.class, Float.class, Double.class, - BigInteger.class, BigDecimal.class, Boolean.class, Enum.class }; - for (Class clazz : clazzes) { - convertiblePairs.add(new ConvertiblePair(CouchbaseDocument.class, clazz)); - } - return convertiblePairs; - } - - @Override - public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - return source == null ? null - : new String(cryptoManager.decrypt(((CouchbaseDocument) source).getContent()), StandardCharsets.UTF_8); - } - - @Override - public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { - return targetType.hasAnnotation(Encrypted.class); - } -} diff --git a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/Encrypted.java b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/Encrypted.java deleted file mode 100644 index fe10c2908..000000000 --- a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/Encrypted.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.core.convert; - -import java.util.Map; - -/** - * - * @author Michael Reiche - */ -public class Encrypted { - Map encryptionMap; - - public Encrypted(Map encryptionMap){ - this.encryptionMap = encryptionMap; - } - - Map getEncryptionMap(){ - return encryptionMap; - } -} diff --git a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/EncryptingWritingConverter.java b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/EncryptingWritingConverter.java deleted file mode 100644 index eeb7240e7..000000000 --- a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/EncryptingWritingConverter.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.core.convert; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.ConditionalGenericConverter; -import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.data.convert.WritingConverter; - -import com.couchbase.client.core.encryption.CryptoManager; - -/** - * Use the cryptomManager to encrypt properties - * - * @author Michael Reiche - */ -@WritingConverter -public class EncryptingWritingConverter implements ConditionalGenericConverter { - - CryptoManager cryptoManager; - - public EncryptingWritingConverter(CryptoManager cryptoManager) { - this.cryptoManager = cryptoManager; - } - - @Override - public Set getConvertibleTypes() { - - Set convertiblePairs = new HashSet<>(); - Class[] clazzes = new Class[] { String.class, Integer.class, Long.class, Float.class, Double.class, - BigInteger.class, BigDecimal.class, Boolean.class, Enum.class }; - for (Class clazz : clazzes) { - convertiblePairs.add(new ConvertiblePair(clazz, String.class)); - } - return convertiblePairs; - } - - @Override - public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - if (source == null) { - return null; - } - com.couchbase.client.java.encryption.annotation.Encrypted ann = sourceType - .getAnnotation(com.couchbase.client.java.encryption.annotation.Encrypted.class); - Map result = new HashMap<>(); - result.putAll(cryptoManager.encrypt(source.toString().getBytes(StandardCharsets.UTF_8), ann.encrypter())); - return new Encrypted(result); - } - - @Override - public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { - return sourceType.hasAnnotation(com.couchbase.client.java.encryption.annotation.Encrypted.class); - } -} diff --git a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java index 6e9e41a74..0a4db7604 100644 --- a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java +++ b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java @@ -19,11 +19,9 @@ import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.UNIQUE; import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.USE_ATTRIBUTES; -import java.beans.Transient; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -35,11 +33,9 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.core.CollectionFactory; import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.data.annotation.Transient; import org.springframework.data.convert.CustomConversions; -import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService; -import org.springframework.data.couchbase.core.convert.translation.TranslationService; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; import org.springframework.data.couchbase.core.mapping.CouchbaseList; import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; @@ -76,9 +72,6 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; -import com.couchbase.client.core.encryption.CryptoManager; -import com.couchbase.client.java.json.JsonObject; - /** * A mapping converter for Couchbase. The converter is responsible for reading from and writing to entities and * converting it into a consumable database representation. @@ -126,11 +119,6 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem */ private @Nullable EntityCallbacks entityCallbacks; - /** - * CryptoManager for en/decryption - */ - private @Nullable CryptoManager cryptoManager; - public MappingCouchbaseConverter() { this(new CouchbaseMappingContext(), null); } @@ -168,23 +156,6 @@ public MappingCouchbaseConverter( spELContext = new SpELContext(CouchbaseDocumentPropertyAccessor.INSTANCE); } - /** - * Get the cryptoManager used in conversions. We need to be able to mangle a property name. - */ - @Override - public void setCustomConversions(CustomConversions customConversions) { - super.setCustomConversions(customConversions); - if (customConversions instanceof CouchbaseCustomConversions) { - this.cryptoManager = ((CouchbaseCustomConversions) customConversions).getCryptoManager(); - } - } - - @Override - public void afterPropertiesSet() { - super.afterPropertiesSet(); - - } - /** * Returns a collection from the given source object. * @@ -307,15 +278,8 @@ public void doWithPersistentProperty(final CouchbasePersistentProperty prop) { accessor.setProperty(prop, obj); } - /** - * doesPropertyExistInSource. It could have getFieldName() or mangled(getFieldName()) - * - * @param property - * @return - */ private boolean doesPropertyExistInSource(final CouchbasePersistentProperty property) { - return property.isIdProperty() || source.containsKey(property.getFieldName()) - || (cryptoManager != null && source.containsKey(cryptoManager.mangle(property.getFieldName()))); + return property.isIdProperty() || source.containsKey(property.getFieldName()); } private boolean isIdConstructionProperty(final CouchbasePersistentProperty property) { @@ -434,23 +398,8 @@ private Object getPotentiallyConvertedSimpleRead(final Object value, final Class throw new MappingException("Unable to create class from " + value.toString()); } } - return target.isAssignableFrom(value.getClass()) ? value : conversionService.convert(value, target); - } - /** - * Potentially convert simple values like ENUMs. - * - * @param value the value to convert. - * @param target the target persistent property which may have an Encrypt annotation - * @return the potentially converted object. - */ - @SuppressWarnings("unchecked") - private Object getPotentiallyConvertedSimpleRead(Object value, final CouchbasePersistentProperty target) { - if (value == null || target == null) { - return value; - } - // this call to convert takes TypeDescriptors - the target type descriptor may have an Encrypt annotation. - return conversionService.convert(value, TypeDescriptor.forObject(value), new TypeDescriptor(target.getField())); + return target.isAssignableFrom(value.getClass()) ? value : conversionService.convert(value, target); } @Override @@ -466,8 +415,7 @@ public void write(final Object source, final CouchbaseDocument target) { typeMapper.writeType(type, target); } - // CouchbasePersistentEntity entity = mappingContext.getPersistentEntity(source.getClass()); - writeInternalRoot(source, target, type, true); + writeInternal(source, target, type, true); if (target.getId() == null) { throw new MappingException("An ID property is needed, but not found/could not be generated on this entity."); } @@ -478,10 +426,10 @@ public void write(final Object source, final CouchbaseDocument target) { * * @param source the source object. * @param target the target document. - * @param withId write out with the id. + * @param typeHint the type information for the source. */ @SuppressWarnings("unchecked") - protected void writeInternalRoot(final Object source, CouchbaseDocument target, TypeInformation typeHint, + protected void writeInternal(final Object source, CouchbaseDocument target, final TypeInformation typeHint, boolean withId) { if (source == null) { return; @@ -494,7 +442,7 @@ protected void writeInternalRoot(final Object source, CouchbaseDocument target, } if (Map.class.isAssignableFrom(source.getClass())) { - writeMapInternal((Map) source, target, ClassTypeInformation.MAP, null); + writeMapInternal((Map) source, target, ClassTypeInformation.MAP); return; } @@ -503,7 +451,7 @@ protected void writeInternalRoot(final Object source, CouchbaseDocument target, } CouchbasePersistentEntity entity = mappingContext.getPersistentEntity(source.getClass()); - writeInternalEntity(source, target, entity, withId, null); + writeInternal(source, target, entity, withId); addCustomTypeKeyIfNecessary(typeHint, source, target); } @@ -539,8 +487,8 @@ private String convertToString(Object propertyObj) { * @param entity the persistent entity to convert from. * @param withId one of the top-level properties is the id for the document */ - protected void writeInternalEntity(final Object source, final CouchbaseDocument target, - final CouchbasePersistentEntity entity, boolean withId, CouchbasePersistentProperty prop) { + protected void writeInternal(final Object source, final CouchbaseDocument target, + final CouchbasePersistentEntity entity, boolean withId) { if (source == null) { return; } @@ -560,7 +508,6 @@ protected void writeInternalEntity(final Object source, final CouchbaseDocument target.setExpiration((int) (entity.getExpiryDuration().getSeconds())); - // write all the entity.properties to the target. Does not write the id or version. writeToTargetDocument(target, entity, accessor, idProperty, versionProperty, prefixes, suffixes, idAttributes); if (idProperty != null && target.getId() == null) { @@ -574,7 +521,6 @@ protected void writeInternalEntity(final Object source, final CouchbaseDocument } else { target.setId(id); } - } entity.doWithAssociations(new AssociationHandler() { @@ -584,7 +530,7 @@ public void doWithAssociation(final Association ass Class type = inverseProp.getType(); Object propertyObj = accessor.getProperty(inverseProp, type); if (null != propertyObj) { - writePropertyInternal(propertyObj, target, inverseProp, accessor); + writePropertyInternal(propertyObj, target, inverseProp, false); } } }); @@ -631,9 +577,9 @@ public void doWithPersistentProperty(final CouchbasePersistentProperty prop) { } if (!conversions.isSimpleType(propertyObj.getClass())) { - writePropertyInternal(propertyObj, target, prop, accessor); + writePropertyInternal(propertyObj, target, prop, false); } else { - writeSimpleInternal(prop, accessor, target, prop.getFieldName()); + writeSimpleInternal(propertyObj, target, prop.getFieldName()); } } } @@ -649,7 +595,7 @@ public void doWithPersistentProperty(final CouchbasePersistentProperty prop) { */ @SuppressWarnings("unchecked") private void writePropertyInternal(final Object source, final CouchbaseDocument target, - final CouchbasePersistentProperty prop, final ConvertingPropertyAccessor accessor) { + final CouchbasePersistentProperty prop, boolean withId) { if (source == null) { return; } @@ -657,23 +603,28 @@ private void writePropertyInternal(final Object source, final CouchbaseDocument String name = prop.getFieldName(); TypeInformation valueType = ClassTypeInformation.from(source.getClass()); TypeInformation type = prop.getTypeInformation(); + if (valueType.isCollectionLike()) { - CouchbaseList collectionDoc = createCollection(asCollection(source), valueType, prop, accessor); - // TODO needs to handle enc + CouchbaseList collectionDoc = createCollection(asCollection(source), prop); target.put(name, collectionDoc); return; } if (valueType.isMap()) { CouchbaseDocument mapDoc = createMap((Map) source, prop); - // TODO needs to handle enc target.put(name, mapDoc); return; } if (valueType.getType().equals(java.util.Optional.class)) { - Optional o = (Optional) source; - writeSimpleInternal(o.map(s -> prop).orElse(null), accessor, target, prop.getFieldName()); + if (source == null) + return; + Optional o = (Optional) source; + if (o.isPresent()) { + writeSimpleInternal(o.get(), target, prop.getFieldName()); + } else { + writeSimpleInternal(null, target, prop.getFieldName()); + } return; } @@ -692,19 +643,9 @@ private void writePropertyInternal(final Object source, final CouchbaseDocument CouchbasePersistentEntity entity = isSubtype(prop.getType(), source.getClass()) ? mappingContext.getRequiredPersistentEntity(source.getClass()) - : mappingContext.getRequiredPersistentEntity(prop); - writeInternalEntity(source, propertyDoc, entity, false, prop); - com.couchbase.client.java.encryption.annotation.Encrypted ann = prop - .findAnnotation(com.couchbase.client.java.encryption.annotation.Encrypted.class); - if (ann != null) { - JsonObject jo = JsonObject.from(propertyDoc.getContent()); - Map encMap = new HashMap(); - encMap.putAll(cryptoManager.encrypt(jo.toBytes(), ann.encrypter())); - CouchbaseDocument mapDoc = writeMapInternal(encMap, new CouchbaseDocument(), prop.getTypeInformation(), prop); - target.put(cryptoManager.mangle(name), mapDoc); - } else { - target.put(name, propertyDoc); - } + : mappingContext.getRequiredPersistentEntity(type); + writeInternal(source, propertyDoc, entity, false); + target.put(name, propertyDoc); } /** @@ -718,7 +659,7 @@ private CouchbaseDocument createMap(final Map map, final Couchba Assert.notNull(map, "Given map must not be null!"); Assert.notNull(prop, "PersistentProperty must not be null!"); - return writeMapInternal(map, new CouchbaseDocument(), prop.getTypeInformation(), prop); + return writeMapInternal(map, new CouchbaseDocument(), prop.getTypeInformation()); } /** @@ -726,10 +667,11 @@ private CouchbaseDocument createMap(final Map map, final Couchba * * @param source the source object. * @param target the target document. + * @param type the type information for the document. * @return the written couchbase document. */ private CouchbaseDocument writeMapInternal(final Map source, final CouchbaseDocument target, - TypeInformation type, CouchbasePersistentProperty prop) { + final TypeInformation type) { for (Map.Entry entry : source.entrySet()) { Object key = entry.getKey(); Object val = entry.getValue(); @@ -738,14 +680,14 @@ private CouchbaseDocument writeMapInternal(final Map source, fin String simpleKey = key.toString(); if (val == null || conversions.isSimpleType(val.getClass())) { - writeSimpleInternal(val, target, simpleKey); // this is an entry in a map, cannot have an annotation + writeSimpleInternal(val, target, simpleKey); } else if (val instanceof Collection || val.getClass().isArray()) { - target.put(simpleKey, - writeCollectionInternal(asCollection(val), new CouchbaseList(conversions.getSimpleTypeHolder()), - prop.getTypeInformation(), prop, getPropertyAccessor(val))); + target.put(simpleKey, writeCollectionInternal(asCollection(val), + new CouchbaseList(conversions.getSimpleTypeHolder()), type.getMapValueType())); } else { CouchbaseDocument embeddedDoc = new CouchbaseDocument(); - writeInternalRoot(val, embeddedDoc, prop.getTypeInformation(), false); + TypeInformation valueTypeInfo = type.isMap() ? type.getMapValueType() : ClassTypeInformation.OBJECT; + writeInternal(val, embeddedDoc, valueTypeInfo, false); target.put(simpleKey, embeddedDoc); } } else { @@ -760,12 +702,12 @@ private CouchbaseDocument writeMapInternal(final Map source, fin * Helper method to create the underlying collection/list. * * @param collection the collection to write. + * @param prop the property information. * @return the created couchbase list. */ - private CouchbaseList createCollection(final Collection collection, final TypeInformation type, - CouchbasePersistentProperty prop, ConvertingPropertyAccessor accessor) { - return writeCollectionInternal(collection, new CouchbaseList(conversions.getSimpleTypeHolder()), type, prop, - accessor); + private CouchbaseList createCollection(final Collection collection, final CouchbasePersistentProperty prop) { + return writeCollectionInternal(collection, new CouchbaseList(conversions.getSimpleTypeHolder()), + prop.getTypeInformation()); } /** @@ -773,11 +715,11 @@ private CouchbaseList createCollection(final Collection collection, final Typ * * @param source the source object. * @param target the target document. + * @param type the type information for the document. * @return the created couchbase list. */ private CouchbaseList writeCollectionInternal(final Collection source, final CouchbaseList target, - final TypeInformation type, CouchbasePersistentProperty prop, ConvertingPropertyAccessor accessor) { - + final TypeInformation type) { TypeInformation componentType = type == null ? null : type.getComponentType(); for (Object element : source) { @@ -787,10 +729,11 @@ private CouchbaseList writeCollectionInternal(final Collection source, final target.put(getPotentiallyConvertedSimpleWrite(element)); } else if (element instanceof Collection || elementType.isArray()) { target.put(writeCollectionInternal(asCollection(element), new CouchbaseList(conversions.getSimpleTypeHolder()), - type, prop, accessor)); + componentType)); } else { + CouchbaseDocument embeddedDoc = new CouchbaseDocument(); - writeInternalRoot(element, embeddedDoc, prop.getTypeInformation(), false); + writeInternal(element, embeddedDoc, componentType, false); target.put(embeddedDoc); } @@ -841,7 +784,7 @@ private Object readCollection(final TypeInformation targetType, final Couchba /** * Write the given source into the couchbase document target. * - * @param source the source object. This does not have access to annotaions. + * @param source the source object. * @param target the target document. * @param key the key of the object. */ @@ -849,41 +792,8 @@ private void writeSimpleInternal(final Object source, final CouchbaseDocument ta target.put(key, getPotentiallyConvertedSimpleWrite(source)); } - /** - * Write the given source into the couchbase document target. - * - * @param source the source persistent property. This has access to annotations. - * @param target the target document. - * @param key the key of the object. - */ - private void writeSimpleInternal(final CouchbasePersistentProperty source, - final ConvertingPropertyAccessor accessor, final CouchbaseDocument target, final String key) { - Object result = getPotentiallyConvertedSimpleWrite(source, accessor); - if (result instanceof Optional) { - Optional optional = (Optional) result; - result = optional.orElse(null); - } - // For FLE, write as "encrypted$"... - if (source.findAnnotation(com.couchbase.client.java.encryption.annotation.Encrypted.class) != null) { - if (result instanceof Encrypted) { - CouchbaseDocument mapDoc = writeMapInternal(((Encrypted) result).getEncryptionMap(), new CouchbaseDocument(), - ClassTypeInformation.MAP, null); - target.put(cryptoManager.mangle(key), mapDoc); - } else { - throw new RuntimeException("field annotation with @Encrypted could not be encrypted " + source.getField()); - } - } else { - target.put(key, result); - } - } - public Object getPotentiallyConvertedSimpleWrite(final Object value) { - return convertForWriteIfNeeded(value); // cannot access annotations - } - - public Object getPotentiallyConvertedSimpleWrite(final CouchbasePersistentProperty value, - ConvertingPropertyAccessor accessor) { - return convertForWriteIfNeeded(value, accessor); // can access annotations + return convertForWriteIfNeeded(value); } /** @@ -935,7 +845,7 @@ public void setEntityCallbacks(EntityCallbacks entityCallbacks) { * @return the converted object. */ @SuppressWarnings("unchecked") - private R readValue(Object value, TypeInformation type, Object parent) { + private R readValue(Object value, TypeInformation type, Object parent) { Class rawType = type.getType(); if (conversions.hasCustomReadTarget(value.getClass(), rawType)) { @@ -945,43 +855,7 @@ private R readValue(Object value, TypeInformation type, Object parent) { } else if (value instanceof CouchbaseList) { return (R) readCollection(type, (CouchbaseList) value, parent); } else { - return (R) getPotentiallyConvertedSimpleRead(value, type.getType()); // type does not have annotations - } - } - - TranslationService translationService = new JacksonTranslationService(); - - /** - * Helper method to read the value based on the PersistentProperty - * - * @param value the value to convert. - * @param prop the persistent property - will have annotations (i.e. Encrypt for FLE) - * @param parent the optional parent. - * @param the target type. - * @return the converted object. - */ - @SuppressWarnings("unchecked") - private R readValue(Object value, CouchbasePersistentProperty prop, Object parent) { - Class rawType = prop.getType(); - - if (prop.findAnnotation(com.couchbase.client.java.encryption.annotation.Encrypted.class) != null - && !conversions.isSimpleType(rawType)) { - TreeMap exported = ((CouchbaseDocument) value).export(); // should be couchbase document (?) - byte[] result = cryptoManager.decrypt(exported); - CouchbaseDocument converted = new CouchbaseDocument(); - Object readEntity = read(prop.getTypeInformation().getType(), - (CouchbaseDocument) translationService.decode(new String(result), converted)); - return (R) readEntity; - } - if (conversions.hasCustomReadTarget(value.getClass(), rawType)) { - TypeInformation ti = ClassTypeInformation.from(value.getClass()); - return (R) conversionService.convert(value, ti.toTypeDescriptor(), new TypeDescriptor(prop.getField())); - } else if (value instanceof CouchbaseDocument) { - return (R) read(prop.getTypeInformation(), (CouchbaseDocument) value, parent); - } else if (value instanceof CouchbaseList) { - return (R) readCollection(prop.getTypeInformation(), (CouchbaseList) value, parent); - } else { - return (R) getPotentiallyConvertedSimpleRead(value, prop);// passes PersistentProperty with annotations + return (R) getPotentiallyConvertedSimpleRead(value, rawType); } } @@ -1087,11 +961,6 @@ public CouchbasePropertyValueProvider(final CouchbaseDocument source, public R getPropertyValue(final CouchbasePersistentProperty property) { String expression = property.getSpelExpression(); Object value = expression != null ? evaluator.evaluate(expression) : source.get(property.getFieldName()); - if (value == null - && property.findAnnotation(com.couchbase.client.java.encryption.annotation.Encrypted.class) != null - && cryptoManager != null) { - value = source.get(cryptoManager.mangle(property.getFieldName())); - } if (property == entity.getIdProperty() && parent == null) { return readValue(source.getId(), property.getTypeInformation(), source); @@ -1099,7 +968,8 @@ public R getPropertyValue(final CouchbasePersistentProperty property) { if (value == null) { return null; } - return readValue(value, property, source); + + return readValue(value, property.getTypeInformation(), source); } } diff --git a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java index a7a0503cf..8e95fdb6e 100644 --- a/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java +++ b/spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java @@ -23,9 +23,7 @@ import java.util.List; import java.util.UUID; -import com.couchbase.client.core.encryption.CryptoManager; import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.converter.GenericConverter; import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; @@ -45,6 +43,7 @@ private OtherConverters() {} */ public static Collection> getConvertersToRegister() { List> converters = new ArrayList>(); + converters.add(UuidToString.INSTANCE); converters.add(StringToUuid.INSTANCE); converters.add(BigIntegerToString.INSTANCE); @@ -55,19 +54,6 @@ private OtherConverters() {} return converters; } - /** - * Returns the crypto converters to be registered. - * - * @param cryptoManager to use for encrypting and decrypting - * @return the list of converters to register. - */ - public static Collection getCryptoConverters(CryptoManager cryptoManager) { - List converters = new ArrayList<>(); - converters.add(new EncryptingWritingConverter(cryptoManager)); - converters.add(new DecryptingReadingConverter(cryptoManager)); - return converters; - } - @WritingConverter public enum UuidToString implements Converter { INSTANCE; diff --git a/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/Address.java b/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/Address.java index 5b5f5d6d5..59b5313b2 100644 --- a/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/Address.java +++ b/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/Address.java @@ -15,16 +15,10 @@ */ package org.springframework.data.couchbase.domain; -import com.couchbase.client.java.encryption.annotation.Encrypted; import org.springframework.data.couchbase.core.mapping.Document; -import java.io.Serializable; - @Document -public class Address extends ComparableEntity implements Serializable { - - @Encrypted - private String encStreet; +public class Address extends ComparableEntity { private String street; private String city; @@ -42,14 +36,6 @@ public void setStreet(String street) { this.street = street; } - public String getEncStreet() { - return encStreet; - } - - public void setEncStreet(String encStreet) { - this.encStreet = encStreet; - } - public String getCity() { return city; } diff --git a/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/Config.java b/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/Config.java index 51f15bcf5..209b90bdc 100644 --- a/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/Config.java +++ b/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/Config.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors + * Copyright 2012-2021 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -220,7 +220,6 @@ public MappingCouchbaseConverter mappingCouchbaseConverter(CouchbaseMappingConte @Bean(name = "couchbaseTranslationService") public TranslationService couchbaseTranslationService() { final JacksonTranslationService jacksonTranslationService = new JacksonTranslationService(); - jacksonTranslationService.setObjectMapper(couchbaseObjectMapper(cryptoManager())); jacksonTranslationService.afterPropertiesSet(); // for sdk3, we need to ask the mapper _it_ uses to ignore extra fields... diff --git a/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/UserEncrypted.java b/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/UserEncrypted.java deleted file mode 100644 index 48fb356f7..000000000 --- a/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/UserEncrypted.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.couchbase.domain; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -import com.couchbase.client.java.encryption.annotation.Encrypted; -import org.springframework.data.annotation.CreatedBy; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.LastModifiedBy; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.annotation.Transient; -import org.springframework.data.annotation.TypeAlias; -import org.springframework.data.annotation.Version; -import org.springframework.data.couchbase.core.mapping.Document; - -/** - * UserEncrypted entity for tests - * - * @author Michael Nitschinger - * @author Michael Reiche - */ - -@Document -@TypeAlias(AbstractingTypeMapper.Type.ABSTRACTUSER) -public class UserEncrypted extends AbstractUser implements Serializable { - - public UserEncrypted(){ - this.subtype = AbstractingTypeMapper.Type.USER; - } - - public UserEncrypted( final String lastname, final String encryptedField) { - this(); - this.id = UUID.randomUUID().toString(); - this.lastname = lastname; - this.encryptedField = encryptedField; - } - - @PersistenceConstructor - public UserEncrypted(final String id, final String firstname, final String lastname) { - this(); - this.id = id; - this.firstname = firstname; - this.lastname = lastname; - } - - public UserEncrypted(final String id, final String firstname, final String lastname, final String encryptedField) { - this(); - this.id = id; - this.firstname = firstname; - this.lastname = lastname; - this.encryptedField = encryptedField; - } - - @Version protected long version; - @Transient protected String transientInfo; - @CreatedBy protected String createdBy; - @CreatedDate protected long createdDate; - @LastModifiedBy protected String lastModifiedBy; - @LastModifiedDate protected long lastModifiedDate; - @Encrypted public String encryptedField; - @Encrypted public Integer encInteger=1; - @Encrypted public Long encLong=Long.valueOf(1); - @Encrypted public Boolean encBoolean = Boolean.TRUE; - - List nicknames = List.of("Happy", "Sleepy"); - Address homeAddress = new Address(); - List
addresses = new ArrayList<>(); - - @Encrypted - Address encAddress = new Address(); - - public String getLastname() { - return lastname; - } - - public long getCreatedDate() { - return createdDate; - } - - public void setCreatedDate(long createdDate) { - this.createdDate = createdDate; - } - - public String getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(String createdBy) { - this.createdBy = createdBy; - } - - public long getLastModifiedDate() { - return lastModifiedDate; - } - - public String getLastModifiedBy() { - return lastModifiedBy; - } - - - public String getEncryptedField() { - return encryptedField; - } - - - public long getVersion() { - return version; - } - - public void setVersion(long version) { - this.version = version; - } - - public void setHomeAddress(Address address){ - this.homeAddress = address; - } - - public void setEncAddress(Address address){ - this.encAddress = address; - } - - public void addAddress(Address address){ - this.addresses.add(address); - } - @Override - public int hashCode() { - return Objects.hash(getId(), firstname, lastname); - } - - public String getTransientInfo() { - return transientInfo; - } - - public void setTransientInfo(String something) { - transientInfo = something; - } - -} diff --git a/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/UserEncryptedRepository.java b/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/UserEncryptedRepository.java deleted file mode 100644 index 2d9bfd20f..000000000 --- a/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/domain/UserEncryptedRepository.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.couchbase.domain; - -import java.util.List; -import java.util.stream.Stream; - -import org.springframework.cache.annotation.Cacheable; -import org.springframework.data.couchbase.repository.Collection; -import org.springframework.data.couchbase.repository.CouchbaseRepository; -import org.springframework.data.couchbase.repository.Query; -import org.springframework.data.couchbase.repository.ScanConsistency; -import org.springframework.data.couchbase.repository.Scope; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.query.QueryScanConsistency; - -/** - * User Repository for tests - * - * @author Michael Nitschinger - * @author Michael Reiche - */ -@Repository -@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) -public interface UserEncryptedRepository extends CouchbaseRepository { - - List findByFirstname(String firstname); - - List findByFirstnameIgnoreCase(String firstname); - - Stream findByLastname(String lastname); - - List findByFirstnameIn(String... firstnames); - - List findByFirstnameIn(JsonArray firstnames); - - List findByFirstnameAndLastname(String firstname, String lastname); - - @Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and firstname = $1 and lastname = $2") - List getByFirstnameAndLastname(String firstname, String lastname); - - @Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and (firstname = $first or lastname = $last)") - List getByFirstnameOrLastname(@Param("first") String firstname, @Param("last") String lastname); - - List findByIdIsNotNullAndFirstnameEquals(String firstname); - - List findByVersionEqualsAndFirstnameEquals(Long version, String firstname); - - @Query("#{#n1ql.selectEntity}|#{#n1ql.filter}|#{#n1ql.bucket}|#{#n1ql.scope}|#{#n1ql.collection}") - @Scope("thisScope") - @Collection("thisCollection") - List spelTests(); - - // simulate a slow operation - @Cacheable("mySpringCache") - default List getByFirstname(String firstname) { - try { - Thread.sleep(1000 * 5); - } catch (InterruptedException ie) {} - return findByFirstname(firstname); - } - - @Override - UserEncrypted save(UserEncrypted user); -} diff --git a/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryFieldLevelEncryptionIntegrationTests.java b/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryFieldLevelEncryptionIntegrationTests.java deleted file mode 100644 index bc17cb86b..000000000 --- a/spring-data-couchbase/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryFieldLevelEncryptionIntegrationTests.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.couchbase.repository; - -import static com.couchbase.client.core.util.CbCollections.mapOf; -import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.UnsupportedEncodingException; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.Provider; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -import com.couchbase.client.encryption.AeadAes256CbcHmacSha512Provider; -import com.couchbase.client.encryption.Decrypter; -import com.couchbase.client.encryption.DefaultCryptoManager; -import com.couchbase.client.encryption.Encrypter; -import com.couchbase.client.encryption.EncryptionResult; -import com.couchbase.client.encryption.Keyring; -import com.couchbase.client.java.encryption.annotation.Encrypted; -import com.couchbase.client.java.encryption.databind.jackson.EncryptionModule; -import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.json.JsonObject; -import com.couchbase.client.java.json.JsonValue; -import com.couchbase.client.java.json.JsonValueModule; -import com.couchbase.client.java.query.QueryOptions; -import com.couchbase.client.java.query.QueryScanConsistency; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; -import org.springframework.data.couchbase.core.CouchbaseTemplate; -import org.springframework.data.couchbase.core.query.N1QLExpression; -import org.springframework.data.couchbase.core.query.N1QLQuery; -import org.springframework.data.couchbase.domain.Address; -import org.springframework.data.couchbase.domain.PersonValueRepository; -import org.springframework.data.couchbase.domain.User; -import org.springframework.data.couchbase.domain.UserEncrypted; -import org.springframework.data.couchbase.domain.UserEncryptedRepository; -import org.springframework.data.couchbase.domain.UserRepository; -import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories; -import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests; -import org.springframework.data.couchbase.util.ClusterType; -import org.springframework.data.couchbase.util.IgnoreWhen; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; - -import com.couchbase.client.core.deps.io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import com.couchbase.client.core.encryption.CryptoManager; -import com.couchbase.client.core.env.SecurityConfig; -import com.couchbase.client.java.env.ClusterEnvironment; -import org.testcontainers.shaded.org.bouncycastle.asn1.isismtt.x509.AdditionalInformationSyntax; - -/** - * Repository KV tests - * - * @author Michael Nitschinger - * @author Michael Reiche - */ -@SpringJUnitConfig(CouchbaseRepositoryFieldLevelEncryptionIntegrationTests.Config.class) -@IgnoreWhen(clusterTypes = ClusterType.MOCKED) -public class CouchbaseRepositoryFieldLevelEncryptionIntegrationTests extends ClusterAwareIntegrationTests { - - @Autowired - UserEncryptedRepository userEncryptedRepository; - - @Autowired PersonValueRepository personValueRepository; - @Autowired CouchbaseTemplate couchbaseTemplate; - - @BeforeEach - public void beforeEach() { - super.beforeEach(); - couchbaseTemplate.removeByQuery(UserEncrypted.class).withConsistency(REQUEST_PLUS).all(); - couchbaseTemplate.findByQuery(UserEncrypted.class).withConsistency(REQUEST_PLUS).all(); - } - - @Test - void javaSDKEncryption() { - - } - - - @Test - @IgnoreWhen(clusterTypes = ClusterType.MOCKED) - void saveAndFindById() { - UserEncrypted user = new UserEncrypted(UUID.randomUUID().toString(), "saveAndFindById", "l", "hello"); - Address address = new Address(); // plaintext address with encrypted street - address.setEncStreet("Olcott Street"); - address.setCity("Santa Clara"); - user.addAddress(address); - user.setHomeAddress(address); - // cannot set encrypted fields within encrypted objects (i.e. setEncAddress()) - Address encAddress = new Address(); // encrypted address with plaintext street. - encAddress.setStreet("Castro St"); - encAddress.setCity("Mountain View"); - user.setEncAddress(encAddress); - assertFalse(userEncryptedRepository.existsById(user.getId())); - userEncryptedRepository.save(user); - Optional found = userEncryptedRepository.findById(user.getId()); - assertTrue(found.isPresent()); - System.err.println(found.get()); - found.ifPresent(u -> assertEquals(user, u)); - assertTrue(userEncryptedRepository.existsById(user.getId())); - //userEncryptedRepository.delete(user); - } - - @Configuration - @EnableCouchbaseRepositories("org.springframework.data.couchbase") - static class Config extends AbstractCouchbaseConfiguration { - - @Override - public String getConnectionString() { - return connectionString(); - } - - @Override - public String getUserName() { - return config().adminUsername(); - } - - @Override - public String getPassword() { - return config().adminPassword(); - } - - @Override - public String getBucketName() { - return bucketName(); - } - - @Override - protected void configureEnvironment(ClusterEnvironment.Builder builder) { - if (config().isUsingCloud()) { - builder.securityConfig( - SecurityConfig.builder().trustManagerFactory(InsecureTrustManagerFactory.INSTANCE).enableTls(true)); - } - CryptoManager cryptoManager = cryptoManager(); - builder.cryptoManager(cryptoManager).build(); - } - - @Override - protected CryptoManager cryptoManager() { - - Decrypter decrypter = new Decrypter() { - @Override - public String algorithm() { - return "myAlg"; - } - - @Override - public byte[] decrypt(EncryptionResult encrypted) { - return Base64.getDecoder().decode(encrypted.getString("ciphertext")); - } - }; - - Encrypter encrypter = new Encrypter() { - @Override - public EncryptionResult encrypt(byte[] plaintext) { - return EncryptionResult - .fromMap(mapOf("alg", "myAlg", "ciphertext", Base64.getEncoder().encodeToString(plaintext))); - } - }; - Map keyMap = new HashMap(); - keyMap.put("myKey", - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }); - Keyring keyring = Keyring.fromMap(keyMap); - // Provider secProvider; - AeadAes256CbcHmacSha512Provider provider = AeadAes256CbcHmacSha512Provider.builder().keyring(keyring) - /*.securityProvider(secProvider)*/.build(); - return DefaultCryptoManager.builder().decrypter(provider.decrypter()) - .defaultEncrypter(provider.encrypterForKey("myKey")).build(); - } - - byte[] hmacMe(String cbc_secret_key, String cbc_api_message) { - try { - return hmac("hmacSHA256", cbc_secret_key.getBytes("utf-8"), cbc_api_message.getBytes("utf-8")); - } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException ue) { - return null; - } - } - - static byte[] hmac(String algorithm, byte[] key, byte[] message) - throws NoSuchAlgorithmException, InvalidKeyException { - Mac mac = Mac.getInstance(algorithm); - mac.init(new SecretKeySpec(key, algorithm)); - return mac.doFinal(message); - } - - } - -}