You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by ab...@apache.org on 2019/06/27 11:10:15 UTC
[cayenne] 01/05: CAY-2584 Crypto: can't use ColumnSelect with
encrypted columns
This is an automated email from the ASF dual-hosted git repository.
abulatski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit a58466a87ffb48c2abc95a171c93c693f812ea24
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Thu Jun 6 18:09:47 2019 +0300
CAY-2584 Crypto: can't use ColumnSelect with encrypted columns
---
.../reader/CryptoRowReaderFactoryDecorator.java | 155 ++++++++++++++++-----
.../apache/cayenne/crypto/Runtime_AES128_IT.java | 100 +++++++++++++
.../cayenne/access/jdbc/ColumnDescriptor.java | 4 +
.../jdbc/reader/DefaultRowReaderFactory.java | 10 +-
4 files changed, 228 insertions(+), 41 deletions(-)
diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
index f757da8..524a7fa 100644
--- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
+++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
@@ -20,6 +20,7 @@ package org.apache.cayenne.crypto.reader;
import org.apache.cayenne.access.jdbc.ColumnDescriptor;
import org.apache.cayenne.access.jdbc.RowDescriptor;
+import org.apache.cayenne.access.jdbc.reader.DefaultRowReaderFactory;
import org.apache.cayenne.access.jdbc.reader.RowReader;
import org.apache.cayenne.access.jdbc.reader.RowReaderFactory;
import org.apache.cayenne.access.types.ExtendedType;
@@ -27,68 +28,70 @@ import org.apache.cayenne.access.types.ExtendedTypeMap;
import org.apache.cayenne.crypto.map.ColumnMapper;
import org.apache.cayenne.crypto.transformer.MapTransformer;
import org.apache.cayenne.crypto.transformer.TransformerFactory;
+import org.apache.cayenne.crypto.transformer.bytes.BytesDecryptor;
+import org.apache.cayenne.crypto.transformer.bytes.BytesTransformerFactory;
+import org.apache.cayenne.crypto.transformer.value.ValueDecryptor;
+import org.apache.cayenne.crypto.transformer.value.ValueTransformerFactory;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.TypesMapping;
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.query.EntityResultSegment;
import org.apache.cayenne.query.QueryMetadata;
+import org.apache.cayenne.query.ScalarResultSegment;
import java.sql.ResultSet;
import java.util.Map;
-public class CryptoRowReaderFactoryDecorator implements RowReaderFactory {
+public class CryptoRowReaderFactoryDecorator extends DefaultRowReaderFactory {
- private RowReaderFactory delegate;
private TransformerFactory transformerFactory;
private ColumnMapper columnMapper;
+ private BytesTransformerFactory bytesTransformerFactory;
+ private ValueTransformerFactory valueTransformerFactory;
public CryptoRowReaderFactoryDecorator(@Inject RowReaderFactory delegate,
@Inject TransformerFactory transformerFactory,
- @Inject ColumnMapper columnMapper) {
- this.delegate = delegate;
+ @Inject ColumnMapper columnMapper,
+ @Inject BytesTransformerFactory bytesTransformerFactory,
+ @Inject ValueTransformerFactory valueTransformerFactory) {
this.transformerFactory = transformerFactory;
this.columnMapper = columnMapper;
+ this.bytesTransformerFactory = bytesTransformerFactory;
+ this.valueTransformerFactory = valueTransformerFactory;
}
@Override
- public RowReader<?> rowReader(final RowDescriptor descriptor, QueryMetadata queryMetadata, DbAdapter adapter,
+ public RowReader<?> rowReader(RowDescriptor descriptor, QueryMetadata queryMetadata, DbAdapter adapter,
Map<ObjAttribute, ColumnDescriptor> attributeOverrides) {
+ RowDescriptor encryptedRowDescriptor = encryptedRowDescriptor(descriptor, adapter.getExtendedTypes());
+ return super.rowReader(encryptedRowDescriptor, queryMetadata, adapter, attributeOverrides);
+ }
- final RowReader<?> delegateReader = delegate.rowReader(encryptedRowDescriptor(descriptor, adapter.getExtendedTypes()),
- queryMetadata,
- adapter,
- attributeOverrides);
-
- return new RowReader<Object>() {
-
- private boolean decryptorCompiled;
- private MapTransformer decryptor;
-
- private void ensureDecryptorCompiled(Object row) {
- if (!decryptorCompiled) {
- decryptor = transformerFactory.decryptor(descriptor.getColumns(), row);
- decryptorCompiled = true;
- }
- }
-
- @Override
- public Object readRow(ResultSet resultSet) {
- Object row = delegateReader.readRow(resultSet);
-
- ensureDecryptorCompiled(row);
-
- if (decryptor != null) {
-
- @SuppressWarnings({"unchecked", "rawtypes"})
- Map<String, Object> map = (Map) row;
+ @Override
+ protected RowReader<?> createScalarRowReader(RowDescriptor descriptor, QueryMetadata queryMetadata,
+ ScalarResultSegment segment) {
+ RowReader<?> scalarRowReader = super
+ .createScalarRowReader(descriptor, queryMetadata, segment);
+ return new DecoratedScalarRowReader(descriptor.getColumns()[segment.getColumnOffset()], scalarRowReader);
+ }
- decryptor.transform(map);
- }
+ @Override
+ protected RowReader<?> createEntityRowReader(RowDescriptor descriptor, QueryMetadata queryMetadata,
+ EntityResultSegment resultMetadata,
+ PostprocessorFactory postProcessorFactory) {
+ RowReader<?> entityRowReader = super
+ .createEntityRowReader(descriptor, queryMetadata, resultMetadata, postProcessorFactory);
+ return new DecoratedFullRowReader(descriptor, entityRowReader);
+ }
- return row;
- }
- };
+ @Override
+ protected RowReader<?> createFullRowReader(RowDescriptor descriptor, QueryMetadata queryMetadata,
+ PostprocessorFactory postProcessorFactory) {
+ RowReader<?> fullRowReader = super
+ .createFullRowReader(descriptor, queryMetadata, postProcessorFactory);
+ return new DecoratedFullRowReader(descriptor, fullRowReader);
}
protected RowDescriptor encryptedRowDescriptor(RowDescriptor descriptor, ExtendedTypeMap typeMap) {
@@ -121,6 +124,82 @@ public class CryptoRowReaderFactoryDecorator implements RowReaderFactory {
encryptedConverters[i] = t;
}
- return new RowDescriptor(originalColumns, encryptedConverters);
+ return new DecoratedRowDescriptor(descriptor, originalColumns, encryptedConverters);
+ }
+
+ private static class DecoratedRowDescriptor extends RowDescriptor {
+
+ private final RowDescriptor original;
+
+ DecoratedRowDescriptor(RowDescriptor rowDescriptor, ColumnDescriptor[] columns, ExtendedType[] converters) {
+ this.original = rowDescriptor;
+ this.columns = columns;
+ this.converters = converters;
+ }
+
+ public RowDescriptor unwrap() {
+ return original;
+ }
+ }
+
+ private class DecoratedScalarRowReader implements RowReader<Object> {
+ private final RowReader<?> delegateReader;
+ private final ValueDecryptor valueDecryptor;
+ private final BytesDecryptor bytesDecryptor;
+
+ DecoratedScalarRowReader(ColumnDescriptor descriptor, RowReader<?> delegateReader) {
+ this.delegateReader = delegateReader;
+ if(descriptor.getAttribute() != null && columnMapper.isEncrypted(descriptor.getAttribute())) {
+ this.valueDecryptor = valueTransformerFactory.decryptor(descriptor.getAttribute());
+ this.bytesDecryptor = bytesTransformerFactory.decryptor();
+ } else {
+ this.valueDecryptor = null;
+ this.bytesDecryptor = null;
+ }
+ }
+
+ @Override
+ public Object readRow(ResultSet resultSet) {
+ Object value = delegateReader.readRow(resultSet);
+ if(valueDecryptor == null) {
+ return value;
+ }
+ return valueDecryptor.decrypt(bytesDecryptor, value);
+ }
+ }
+
+ private class DecoratedFullRowReader implements RowReader<Object> {
+
+ private final RowDescriptor descriptor;
+ private final RowReader<?> delegateReader;
+ private boolean decryptorCompiled;
+ private MapTransformer decryptor;
+
+ DecoratedFullRowReader(RowDescriptor descriptor, RowReader<?> delegateReader) {
+ this.descriptor = descriptor;
+ this.delegateReader = delegateReader;
+ }
+
+ private void ensureDecryptorCompiled(Object row) {
+ if (!decryptorCompiled) {
+ decryptor = transformerFactory.decryptor(descriptor.getColumns(), row);
+ decryptorCompiled = true;
+ }
+ }
+
+ @Override
+ public Object readRow(ResultSet resultSet) {
+ Object row = delegateReader.readRow(resultSet);
+
+ ensureDecryptorCompiled(row);
+
+ if (decryptor != null) {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> map = (Map<String, Object>) row;
+ decryptor.transform(map);
+ }
+
+ return row;
+ }
}
}
diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java
index 8861b7d..338e9d8 100644
--- a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java
+++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java
@@ -23,6 +23,8 @@ import org.apache.cayenne.crypto.db.Table1;
import org.apache.cayenne.crypto.db.Table2;
import org.apache.cayenne.crypto.transformer.value.IntegerConverter;
import org.apache.cayenne.crypto.unit.CryptoUnitUtils;
+import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.query.ObjectSelect;
import org.apache.cayenne.query.SelectQuery;
import org.junit.Before;
import org.junit.Test;
@@ -156,4 +158,102 @@ public class Runtime_AES128_IT extends Runtime_AES128_Base {
assertEquals(61, result.get(0).getCryptoInt());
}
+ @Test
+ public void test_ColumnQueryObject() {
+
+ ObjectContext context = runtime.newContext();
+
+ Table1 t1 = context.newObject(Table1.class);
+ t1.setCryptoInt(1);
+ t1.setCryptoString("test");
+ context.commitChanges();
+
+ List<Table1> result = ObjectSelect
+ .columnQuery(Table1.class, Property.createSelf(Table1.class))
+ .select(context);
+
+ assertEquals(1, result.size());
+ assertEquals(1, result.get(0).getCryptoInt());
+ assertEquals("test", result.get(0).getCryptoString());
+ }
+
+ @Test
+ public void test_ColumnQueryObjectWithPlainScalar() {
+
+ ObjectContext context = runtime.newContext();
+
+ Table1 t1 = context.newObject(Table1.class);
+ t1.setCryptoInt(1);
+ t1.setPlainInt(2);
+ t1.setCryptoString("test");
+ context.commitChanges();
+
+ List<Object[]> result = ObjectSelect
+ .columnQuery(Table1.class, Property.createSelf(Table1.class), Table1.PLAIN_INT)
+ .select(context);
+
+ assertEquals(1, result.size());
+ assertEquals(1, ((Table1)result.get(0)[0]).getCryptoInt());
+ assertEquals("test", ((Table1)result.get(0)[0]).getCryptoString());
+ assertEquals(2, result.get(0)[1]);
+ }
+
+ @Test
+ public void test_ColumnQueryObjectWithEncryptedScalar() {
+
+ ObjectContext context = runtime.newContext();
+
+ Table1 t1 = context.newObject(Table1.class);
+ t1.setCryptoInt(1);
+ t1.setPlainInt(2);
+ t1.setCryptoString("test");
+ context.commitChanges();
+
+ List<Object[]> result = ObjectSelect
+ .columnQuery(Table1.class, Property.createSelf(Table1.class), Table1.CRYPTO_INT)
+ .select(context);
+
+ assertEquals(1, result.size());
+ assertEquals(1, ((Table1)result.get(0)[0]).getCryptoInt());
+ assertEquals("test", ((Table1)result.get(0)[0]).getCryptoString());
+ assertEquals(1, result.get(0)[1]);
+ }
+
+ @Test
+ public void test_ColumnQuerySingleScalar() {
+ ObjectContext context = runtime.newContext();
+
+ Table1 t1 = context.newObject(Table1.class);
+ t1.setCryptoInt(1);
+ t1.setCryptoString("test");
+ context.commitChanges();
+
+ List<String> result = ObjectSelect
+ .columnQuery(Table1.class, Table1.CRYPTO_STRING)
+ .select(context);
+
+ assertEquals(1, result.size());
+ assertEquals("test", result.get(0));
+ }
+
+ @Test
+ public void test_ColumnQueryMultipleScalars() {
+ ObjectContext context = runtime.newContext();
+
+ Table1 t1 = context.newObject(Table1.class);
+ t1.setCryptoInt(1);
+ t1.setCryptoString("test");
+ t1.setPlainInt(2);
+ context.commitChanges();
+
+ List<Object[]> result = ObjectSelect
+ .columnQuery(Table1.class, Table1.CRYPTO_STRING, Table1.CRYPTO_INT, Table1.PLAIN_INT)
+ .select(context);
+
+ assertEquals(1, result.size());
+ assertEquals("test", result.get(0)[0]);
+ assertEquals(1, result.get(0)[1]);
+ assertEquals(2, result.get(0)[2]);
+ }
+
}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
index abe25d4..a2eeed9 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java
@@ -149,6 +149,10 @@ public class ColumnDescriptor {
return name;
}
+ public void setAttribute(DbAttribute attribute) {
+ this.attribute = attribute;
+ }
+
/**
* Returns a DbAttribute for this column. Since columns descriptors can be
* initialized in a context where a DbAttribite is unknown, this method may
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/reader/DefaultRowReaderFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/reader/DefaultRowReaderFactory.java
index 9ae3139..8229f17 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/reader/DefaultRowReaderFactory.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/jdbc/reader/DefaultRowReaderFactory.java
@@ -70,7 +70,7 @@ public class DefaultRowReaderFactory implements RowReaderFactory {
return createEntityRowReader(descriptor, queryMetadata, (EntityResultSegment) segment,
postProcessorFactory);
} else {
- return new ScalarRowReader<>(descriptor, (ScalarResultSegment) segment);
+ return createScalarRowReader(descriptor, queryMetadata, (ScalarResultSegment) segment);
}
} else {
CompoundRowReader reader = new CompoundRowReader(resultWidth);
@@ -84,7 +84,7 @@ public class DefaultRowReaderFactory implements RowReaderFactory {
createEntityRowReader(descriptor, queryMetadata, (EntityResultSegment) segment,
postProcessorFactory));
} else {
- reader.addRowReader(i, new ScalarRowReader<>(descriptor, (ScalarResultSegment) segment));
+ reader.addRowReader(i, createScalarRowReader(descriptor, queryMetadata, (ScalarResultSegment) segment));
}
}
@@ -92,7 +92,11 @@ public class DefaultRowReaderFactory implements RowReaderFactory {
}
}
- private RowReader<?> createEntityRowReader(RowDescriptor descriptor, QueryMetadata queryMetadata,
+ protected RowReader<?> createScalarRowReader(RowDescriptor descriptor, QueryMetadata queryMetadata, ScalarResultSegment segment) {
+ return new ScalarRowReader<Object>(descriptor, segment);
+ }
+
+ protected RowReader<?> createEntityRowReader(RowDescriptor descriptor, QueryMetadata queryMetadata,
EntityResultSegment resultMetadata, PostprocessorFactory postProcessorFactory) {
if (queryMetadata.getPageSize() > 0) {