Repository: cayenne Updated Branches: refs/heads/master 30c2c6f12 -> fa605bf30
CAY-2103 cayenne-crypto: support for mapping non-String and non-binary types Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/fa605bf3 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/fa605bf3 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/fa605bf3 Branch: refs/heads/master Commit: fa605bf300f95f916e6339c3be3b0f514dca9341 Parents: 30c2c6f Author: Andrus Adamchik <and...@objectstyle.com> Authored: Thu Aug 18 12:25:04 2016 +0300 Committer: Andrus Adamchik <and...@objectstyle.com> Committed: Thu Aug 18 15:44:04 2016 +0300 ---------------------------------------------------------------------- .../reader/CryptoRowReaderFactoryDecorator.java | 60 +++- .../value/Base64StringConverter.java | 2 +- .../transformer/value/BooleanConverter.java | 35 +++ .../crypto/transformer/value/ByteConverter.java | 56 ++++ .../transformer/value/BytesConverter.java | 2 +- .../value/DefaultValueTransformerFactory.java | 313 ++++++++++--------- .../transformer/value/IntegerConverter.java | 71 +++++ .../crypto/transformer/value/LongConverter.java | 58 ++++ .../transformer/value/ShortConverter.java | 68 ++++ .../transformer/value/Utf8StringConverter.java | 4 +- .../transformer/value/UtilDateConverter.java | 30 ++ .../cayenne/crypto/Runtime_AES128_Base.java | 9 +- .../cayenne/crypto/Runtime_AES128_IT.java | 55 +++- .../apache/cayenne/crypto/db/auto/_Table1.java | 18 ++ .../transformer/value/BooleanConverterTest.java | 28 ++ .../transformer/value/ByteConverterTest.java | 45 +++ .../transformer/value/IntegerConverterTest.java | 61 ++++ .../transformer/value/LongConverterTest.java | 49 +++ .../transformer/value/ShortConverterTest.java | 48 +++ .../value/UtilDateConverterTest.java | 31 ++ .../cayenne/crypto/unit/CryptoUnitUtils.java | 35 ++- .../src/test/resources/datamap.map.xml | 4 + .../org/apache/cayenne/dba/TypesMapping.java | 53 +++- docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 + 24 files changed, 951 insertions(+), 185 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java ---------------------------------------------------------------------- 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 fc1f418..ab2c08d 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 @@ -18,36 +18,47 @@ ****************************************************************/ package org.apache.cayenne.crypto.reader; -import java.sql.ResultSet; -import java.util.Map; - import org.apache.cayenne.access.jdbc.ColumnDescriptor; import org.apache.cayenne.access.jdbc.RowDescriptor; import org.apache.cayenne.access.jdbc.reader.RowReader; import org.apache.cayenne.access.jdbc.reader.RowReaderFactory; -import org.apache.cayenne.crypto.transformer.TransformerFactory; +import org.apache.cayenne.access.types.ExtendedType; +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.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.QueryMetadata; +import java.sql.ResultSet; +import java.util.Map; + public class CryptoRowReaderFactoryDecorator implements RowReaderFactory { private RowReaderFactory delegate; private TransformerFactory transformerFactory; + private ColumnMapper columnMapper; public CryptoRowReaderFactoryDecorator(@Inject RowReaderFactory delegate, - @Inject TransformerFactory transformerFactory) { + @Inject TransformerFactory transformerFactory, + @Inject ColumnMapper columnMapper) { this.delegate = delegate; this.transformerFactory = transformerFactory; + this.columnMapper = columnMapper; } @Override public RowReader<?> rowReader(final RowDescriptor descriptor, QueryMetadata queryMetadata, DbAdapter adapter, - Map<ObjAttribute, ColumnDescriptor> attributeOverrides) { + Map<ObjAttribute, ColumnDescriptor> attributeOverrides) { - final RowReader<?> delegateReader = delegate.rowReader(descriptor, queryMetadata, adapter, attributeOverrides); + final RowReader<?> delegateReader = delegate.rowReader(encryptedRowDescriptor(descriptor, adapter.getExtendedTypes()), + queryMetadata, + adapter, + attributeOverrides); return new RowReader<Object>() { @@ -69,7 +80,7 @@ public class CryptoRowReaderFactoryDecorator implements RowReaderFactory { if (decryptor != null) { - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) Map<String, Object> map = (Map) row; decryptor.transform(map); @@ -79,4 +90,37 @@ public class CryptoRowReaderFactoryDecorator implements RowReaderFactory { } }; } + + protected RowDescriptor encryptedRowDescriptor(RowDescriptor descriptor, ExtendedTypeMap typeMap) { + + // need to tweak the original descriptor to ensure binary columns are read as binary, eben if the plain Java + // type is not a byte[] + + ColumnDescriptor[] originalColumns = descriptor.getColumns(); + int len = originalColumns.length; + + ExtendedType[] originalConverters = descriptor.getConverters(); + ExtendedType[] encryptedConverters = new ExtendedType[len]; + + for (int i = 0; i < len; i++) { + DbAttribute attribute = originalColumns[i].getAttribute(); + + ExtendedType t = originalConverters[i]; + + if (attribute != null && columnMapper.isEncrypted(attribute)) { + + // only char or binary columns can store encrypted data + if (TypesMapping.isBinary(attribute.getType())) { + t = typeMap.getRegisteredType(byte[].class); + } else if (TypesMapping.isCharacter(attribute.getType())) { + t = typeMap.getRegisteredType(String.class); + } + // else - warning? + } + + encryptedConverters[i] = t; + } + + return new RowDescriptor(originalColumns, encryptedConverters); + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Base64StringConverter.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Base64StringConverter.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Base64StringConverter.java index 3d36a3a..2f80d6e 100644 --- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Base64StringConverter.java +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Base64StringConverter.java @@ -23,7 +23,7 @@ import javax.xml.bind.DatatypeConverter; /** * @since 4.0 */ -class Base64StringConverter implements BytesConverter { +public class Base64StringConverter implements BytesConverter { static final BytesConverter INSTANCE = new Base64StringConverter(); http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BooleanConverter.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BooleanConverter.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BooleanConverter.java new file mode 100644 index 0000000..cf7636f --- /dev/null +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BooleanConverter.java @@ -0,0 +1,35 @@ +package org.apache.cayenne.crypto.transformer.value; + +/** + * Converts between java.util.Date and byte[], based on the long timestamp encoding. + * + * @since 4.0 + */ +public class BooleanConverter implements BytesConverter { + + static final BytesConverter INSTANCE = new BooleanConverter(); + + @Override + public Object fromBytes(byte[] bytes) { + + if (bytes.length != 1) { + throw new IllegalArgumentException("Unexpected number of bytes for boolean: " + bytes.length); + } + + byte b = bytes[0]; + if (b == 0) { + return Boolean.FALSE; + } else if (b == 1) { + return Boolean.TRUE; + } else { + throw new IllegalArgumentException("Unexpected byte value for boolean: " + b); + } + } + + @Override + public byte[] toBytes(Object value) { + return new byte[]{ + ((Boolean) value) ? (byte) 1 : 0 + }; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ByteConverter.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ByteConverter.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ByteConverter.java new file mode 100644 index 0000000..77f97e0 --- /dev/null +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ByteConverter.java @@ -0,0 +1,56 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.cayenne.crypto.transformer.value; + +/** + * @since 4.0 + */ +public class ByteConverter implements BytesConverter { + + public static final BytesConverter INSTANCE = new ByteConverter(); + private static final int BYTES = 1; + + static byte getByte(byte[] bytes) { + if (bytes.length != BYTES) { + throw new IllegalArgumentException("Unexpected number of bytes: " + bytes.length); + } + + return bytes[0]; + } + + static byte[] getBytes(byte b) { + return new byte[]{b}; + } + + @Override + public Object fromBytes(byte[] bytes) { + return getByte(bytes); + } + + @Override + public byte[] toBytes(Object value) { + + long number = ((Number) value).longValue(); + if (number <= Byte.MAX_VALUE && number >= Byte.MIN_VALUE) { + return getBytes((byte) number); + } + + throw new IllegalArgumentException("Byte is out of range: " + number); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BytesConverter.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BytesConverter.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BytesConverter.java index d6b75f6..1caec01 100644 --- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BytesConverter.java +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BytesConverter.java @@ -21,7 +21,7 @@ package org.apache.cayenne.crypto.transformer.value; /** * @since 4.0 */ -interface BytesConverter { +public interface BytesConverter { Object fromBytes(byte[] bytes); http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactory.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactory.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactory.java index eb6bf6a..26c4a36 100644 --- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactory.java +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactory.java @@ -18,15 +18,6 @@ ****************************************************************/ package org.apache.cayenne.crypto.transformer.value; -import java.security.Key; -import java.sql.Types; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - import org.apache.cayenne.crypto.key.KeySource; import org.apache.cayenne.dba.TypesMapping; import org.apache.cayenne.di.Inject; @@ -36,190 +27,230 @@ import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.ObjAttribute; import org.apache.cayenne.map.ObjEntity; +import java.security.Key; +import java.sql.Types; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + /** * A {@link ValueTransformerFactory} that creates encryptors/decryptors that are * taking advantage of the JCE (Java Cryptography Extension) ciphers. - * + * * @since 4.0 */ public class DefaultValueTransformerFactory implements ValueTransformerFactory { - private final Key defaultKey; + private final Key defaultKey; + + private final Map<String, BytesConverter> objectToBytes; + private final Map<Integer, BytesConverter> dbToBytes; + + private final Map<String, BytesConverter> bytesToObject; + private final Map<Integer, BytesConverter> bytesToDb; + + private final ConcurrentMap<DbAttribute, ValueEncryptor> encryptors; + private final ConcurrentMap<DbAttribute, ValueDecryptor> decryptors; + + public DefaultValueTransformerFactory(@Inject KeySource keySource) { + this.defaultKey = keySource.getKey(keySource.getDefaultKeyAlias()); + + this.encryptors = new ConcurrentHashMap<DbAttribute, ValueEncryptor>(); + this.decryptors = new ConcurrentHashMap<DbAttribute, ValueDecryptor>(); + + this.objectToBytes = createObjectToBytesConverters(); + this.dbToBytes = createDbToBytesConverters(); + this.bytesToObject = createBytesToObjectConverters(); + this.bytesToDb = createBytesToDbConverters(); + } + + @Override + public ValueDecryptor decryptor(DbAttribute a) { + ValueDecryptor e = decryptors.get(a); + + if (e == null) { + + ValueDecryptor newTransformer = createDecryptor(a); + ValueDecryptor oldTransformer = decryptors.putIfAbsent(a, newTransformer); + + e = oldTransformer != null ? oldTransformer : newTransformer; + } + + return e; + } - private final Map<String, BytesConverter> objectToBytes; - private final Map<Integer, BytesConverter> dbToBytes; + @Override + public ValueEncryptor encryptor(DbAttribute a) { + ValueEncryptor e = encryptors.get(a); - private final Map<String, BytesConverter> bytesToObject; - private final Map<Integer, BytesConverter> bytesToDb; + if (e == null) { - private final ConcurrentMap<DbAttribute, ValueEncryptor> encryptors; - private final ConcurrentMap<DbAttribute, ValueDecryptor> decryptors; + ValueEncryptor newTransformer = createEncryptor(a); + ValueEncryptor oldTransformer = encryptors.putIfAbsent(a, newTransformer); - public DefaultValueTransformerFactory(@Inject KeySource keySource) { - this.defaultKey = keySource.getKey(keySource.getDefaultKeyAlias()); + e = oldTransformer != null ? oldTransformer : newTransformer; + } - this.encryptors = new ConcurrentHashMap<DbAttribute, ValueEncryptor>(); - this.decryptors = new ConcurrentHashMap<DbAttribute, ValueDecryptor>(); + return e; + } - this.objectToBytes = createObjectToBytesConverters(); - this.dbToBytes = createDbToBytesConverters(); - this.bytesToObject = createBytesToObjectConverters(); - this.bytesToDb = createBytesToDbConverters(); - } + protected Map<Integer, BytesConverter> createDbToBytesConverters() { + Map<Integer, BytesConverter> map = new HashMap<Integer, BytesConverter>(); - @Override - public ValueDecryptor decryptor(DbAttribute a) { - ValueDecryptor e = decryptors.get(a); + map.put(Types.BINARY, BytesToBytesConverter.INSTANCE); + map.put(Types.BLOB, BytesToBytesConverter.INSTANCE); + map.put(Types.VARBINARY, BytesToBytesConverter.INSTANCE); + map.put(Types.LONGVARBINARY, BytesToBytesConverter.INSTANCE); - if (e == null) { + map.put(Types.CHAR, Base64StringConverter.INSTANCE); + map.put(Types.NCHAR, Base64StringConverter.INSTANCE); + map.put(Types.CLOB, Base64StringConverter.INSTANCE); + map.put(Types.NCLOB, Base64StringConverter.INSTANCE); + map.put(Types.LONGVARCHAR, Base64StringConverter.INSTANCE); + map.put(Types.LONGNVARCHAR, Base64StringConverter.INSTANCE); + map.put(Types.VARCHAR, Base64StringConverter.INSTANCE); + map.put(Types.NVARCHAR, Base64StringConverter.INSTANCE); - ValueDecryptor newTransformer = createDecryptor(a); - ValueDecryptor oldTransformer = decryptors.putIfAbsent(a, newTransformer); + return map; + } - e = oldTransformer != null ? oldTransformer : newTransformer; - } + protected Map<Integer, BytesConverter> createBytesToDbConverters() { + Map<Integer, BytesConverter> map = new HashMap<Integer, BytesConverter>(); - return e; - } + map.put(Types.BINARY, BytesToBytesConverter.INSTANCE); + map.put(Types.BLOB, BytesToBytesConverter.INSTANCE); + map.put(Types.VARBINARY, BytesToBytesConverter.INSTANCE); + map.put(Types.LONGVARBINARY, BytesToBytesConverter.INSTANCE); - @Override - public ValueEncryptor encryptor(DbAttribute a) { - ValueEncryptor e = encryptors.get(a); + map.put(Types.CHAR, Base64StringConverter.INSTANCE); + map.put(Types.NCHAR, Base64StringConverter.INSTANCE); + map.put(Types.CLOB, Base64StringConverter.INSTANCE); + map.put(Types.NCLOB, Base64StringConverter.INSTANCE); + map.put(Types.LONGVARCHAR, Base64StringConverter.INSTANCE); + map.put(Types.LONGNVARCHAR, Base64StringConverter.INSTANCE); + map.put(Types.VARCHAR, Base64StringConverter.INSTANCE); + map.put(Types.NVARCHAR, Base64StringConverter.INSTANCE); - if (e == null) { + return map; + } - ValueEncryptor newTransformer = createEncryptor(a); - ValueEncryptor oldTransformer = encryptors.putIfAbsent(a, newTransformer); + protected Map<String, BytesConverter> createObjectToBytesConverters() { + Map<String, BytesConverter> map = new HashMap<>(); - e = oldTransformer != null ? oldTransformer : newTransformer; - } + map.put("byte[]", BytesToBytesConverter.INSTANCE); + map.put(String.class.getName(), Utf8StringConverter.INSTANCE); - return e; - } + map.put(Long.class.getName(), LongConverter.INSTANCE); + map.put(Long.TYPE.getName(), LongConverter.INSTANCE); - protected Map<Integer, BytesConverter> createDbToBytesConverters() { - Map<Integer, BytesConverter> map = new HashMap<Integer, BytesConverter>(); + map.put(Integer.class.getName(), IntegerConverter.INSTANCE); + map.put(Integer.TYPE.getName(), IntegerConverter.INSTANCE); - map.put(Types.BINARY, BytesToBytesConverter.INSTANCE); - map.put(Types.BLOB, BytesToBytesConverter.INSTANCE); - map.put(Types.VARBINARY, BytesToBytesConverter.INSTANCE); - map.put(Types.LONGVARBINARY, BytesToBytesConverter.INSTANCE); + map.put(Short.class.getName(), ShortConverter.INSTANCE); + map.put(Short.TYPE.getName(), ShortConverter.INSTANCE); - map.put(Types.CHAR, Base64StringConverter.INSTANCE); - map.put(Types.NCHAR, Base64StringConverter.INSTANCE); - map.put(Types.CLOB, Base64StringConverter.INSTANCE); - map.put(Types.NCLOB, Base64StringConverter.INSTANCE); - map.put(Types.LONGVARCHAR, Base64StringConverter.INSTANCE); - map.put(Types.LONGNVARCHAR, Base64StringConverter.INSTANCE); - map.put(Types.VARCHAR, Base64StringConverter.INSTANCE); - map.put(Types.NVARCHAR, Base64StringConverter.INSTANCE); + map.put(Byte.class.getName(), ByteConverter.INSTANCE); + map.put(Byte.TYPE.getName(), ByteConverter.INSTANCE); - return map; - } + map.put(Boolean.class.getName(), BooleanConverter.INSTANCE); + map.put(Date.class.getName(), UtilDateConverter.INSTANCE); - protected Map<Integer, BytesConverter> createBytesToDbConverters() { - Map<Integer, BytesConverter> map = new HashMap<Integer, BytesConverter>(); + return map; + } - map.put(Types.BINARY, BytesToBytesConverter.INSTANCE); - map.put(Types.BLOB, BytesToBytesConverter.INSTANCE); - map.put(Types.VARBINARY, BytesToBytesConverter.INSTANCE); - map.put(Types.LONGVARBINARY, BytesToBytesConverter.INSTANCE); + protected Map<String, BytesConverter> createBytesToObjectConverters() { - map.put(Types.CHAR, Base64StringConverter.INSTANCE); - map.put(Types.NCHAR, Base64StringConverter.INSTANCE); - map.put(Types.CLOB, Base64StringConverter.INSTANCE); - map.put(Types.NCLOB, Base64StringConverter.INSTANCE); - map.put(Types.LONGVARCHAR, Base64StringConverter.INSTANCE); - map.put(Types.LONGNVARCHAR, Base64StringConverter.INSTANCE); - map.put(Types.VARCHAR, Base64StringConverter.INSTANCE); - map.put(Types.NVARCHAR, Base64StringConverter.INSTANCE); + Map<String, BytesConverter> map = new HashMap<>(); - return map; - } + map.put("byte[]", BytesToBytesConverter.INSTANCE); + map.put(String.class.getName(), Utf8StringConverter.INSTANCE); - protected Map<String, BytesConverter> createObjectToBytesConverters() { - Map<String, BytesConverter> map = new HashMap<>(); + map.put(Long.class.getName(), LongConverter.INSTANCE); + map.put(Long.TYPE.getName(), LongConverter.INSTANCE); - map.put("byte[]", BytesToBytesConverter.INSTANCE); - map.put(String.class.getName(), Utf8StringConverter.INSTANCE); + map.put(Integer.class.getName(), IntegerConverter.INSTANCE); + map.put(Integer.TYPE.getName(), IntegerConverter.INSTANCE); - return map; - } + map.put(Short.class.getName(), ShortConverter.INSTANCE); + map.put(Short.TYPE.getName(), ShortConverter.INSTANCE); - protected Map<String, BytesConverter> createBytesToObjectConverters() { + map.put(Byte.class.getName(), ByteConverter.INSTANCE); + map.put(Byte.TYPE.getName(), ByteConverter.INSTANCE); - Map<String, BytesConverter> map = new HashMap<>(); + map.put(Boolean.class.getName(), BooleanConverter.INSTANCE); + map.put(Date.class.getName(), UtilDateConverter.INSTANCE); - map.put("byte[]", BytesToBytesConverter.INSTANCE); - map.put(String.class.getName(), Utf8StringConverter.INSTANCE); + return map; + } - return map; - } + protected ValueEncryptor createEncryptor(DbAttribute a) { - protected ValueEncryptor createEncryptor(DbAttribute a) { + String type = getJavaType(a); - String type = getJavaType(a); + BytesConverter toBytes = objectToBytes.get(type); + if (toBytes == null) { + throw new IllegalArgumentException("The type " + type + " for attribute " + a + + " has no object-to-bytes conversion"); + } - BytesConverter toBytes = objectToBytes.get(type); - if (toBytes == null) { - throw new IllegalArgumentException("The type " + type + " for attribute " + a - + " has no object-to-bytes conversion"); - } + BytesConverter fromBytes = bytesToDb.get(a.getType()); + if (fromBytes == null) { + throw new IllegalArgumentException("The type " + TypesMapping.getSqlNameByType(a.getType()) + + " for attribute " + a + " has no bytes-to-db conversion"); + } - BytesConverter fromBytes = bytesToDb.get(a.getType()); - if (fromBytes == null) { - throw new IllegalArgumentException("The type " + TypesMapping.getSqlNameByType(a.getType()) - + " for attribute " + a + " has no bytes-to-db conversion"); - } + return new DefaultValueEncryptor(toBytes, fromBytes); + } - return new DefaultValueEncryptor(toBytes, fromBytes); - } + protected ValueDecryptor createDecryptor(DbAttribute a) { - protected ValueDecryptor createDecryptor(DbAttribute a) { + BytesConverter toBytes = dbToBytes.get(a.getType()); + if (toBytes == null) { + throw new IllegalArgumentException("The type " + TypesMapping.getSqlNameByType(a.getType()) + + " for attribute " + a + " has no db-to-bytes conversion"); + } - BytesConverter toBytes = dbToBytes.get(a.getType()); - if (toBytes == null) { - throw new IllegalArgumentException("The type " + TypesMapping.getSqlNameByType(a.getType()) - + " for attribute " + a + " has no db-to-bytes conversion"); - } + String type = getJavaType(a); + BytesConverter fromBytes = bytesToObject.get(type); + if (fromBytes == null) { + throw new IllegalArgumentException("The type " + type + " for attribute " + a + + " has no bytes-to-object conversion"); + } - String type = getJavaType(a); - BytesConverter fromBytes = bytesToObject.get(type); - if (fromBytes == null) { - throw new IllegalArgumentException("The type " + type + " for attribute " + a - + " has no bytes-to-object conversion"); - } + return new DefaultValueDecryptor(toBytes, fromBytes, defaultKey); + } - return new DefaultValueDecryptor(toBytes, fromBytes, defaultKey); - } + // TODO: calculating Java type of ObjAttribute may become unneeded per + // CAY-1752, as DbAttribute will have it. + protected String getJavaType(DbAttribute a) { - // TODO: calculating Java type of ObjAttribute may become unneeded per - // CAY-1752, as DbAttribute will have it. - protected String getJavaType(DbAttribute a) { + DbEntity dbEntity = a.getEntity(); + DataMap dataMap = dbEntity.getDataMap(); + Collection<ObjEntity> objEntities = dataMap.getMappedEntities(dbEntity); - DbEntity dbEntity = a.getEntity(); - DataMap dataMap = dbEntity.getDataMap(); - Collection<ObjEntity> objEntities = dataMap.getMappedEntities(dbEntity); - - if (objEntities.size() != 1) { - return TypesMapping.getJavaBySqlType(a.getType()); - } + if (objEntities.size() != 1) { + return TypesMapping.getJavaBySqlType(a.getType()); + } - Collection<String> javaTypes = new HashSet<String>(); - ObjEntity objEntity = objEntities.iterator().next(); - for (ObjAttribute oa : objEntity.getAttributes()) { + Collection<String> javaTypes = new HashSet<String>(); + ObjEntity objEntity = objEntities.iterator().next(); + for (ObjAttribute oa : objEntity.getAttributes()) { - // TODO: this won't pick up flattened attributes - if (a.getName().equals(oa.getDbAttributePath())) { - javaTypes.add(oa.getType()); - } - } + // TODO: this won't pick up flattened attributes + if (a.getName().equals(oa.getDbAttributePath())) { + javaTypes.add(oa.getType()); + } + } - if (javaTypes.size() != 1) { - return TypesMapping.getJavaBySqlType(a.getType()); - } + if (javaTypes.size() != 1) { + return TypesMapping.getJavaBySqlType(a.getType()); + } - return javaTypes.iterator().next(); - } + return javaTypes.iterator().next(); + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/IntegerConverter.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/IntegerConverter.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/IntegerConverter.java new file mode 100644 index 0000000..e95a895 --- /dev/null +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/IntegerConverter.java @@ -0,0 +1,71 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.cayenne.crypto.transformer.value; + +/** + * Converts between integer and byte[] using big-endian encoding. + * + * @since 4.0 + */ +public class IntegerConverter implements BytesConverter { + + public static final BytesConverter INSTANCE = new IntegerConverter(); + private static final int BYTES = 4; + + static int getInt(byte[] bytes) { + if (bytes.length < BYTES) { + return ShortConverter.getShort(bytes); + } + + if (bytes.length > BYTES) { + throw new IllegalArgumentException("byte[] is too large for a single int value: " + bytes.length); + } + + return (bytes[0] & 0xFF) << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF); + } + + static byte[] getBytes(int k) { + + if (k <= Short.MAX_VALUE) { + return ShortConverter.getBytes((short) k); + } + + return new byte[]{ + (byte) (k >> 24), + (byte) (k >> 16), + (byte) (k >> 8), + (byte) k}; + } + + @Override + public Object fromBytes(byte[] bytes) { + return getInt(bytes); + } + + @Override + public byte[] toBytes(Object value) { + + long number = ((Number) value).longValue(); + if (number <= Integer.MAX_VALUE && number >= Integer.MIN_VALUE) { + return getBytes((int) number); + } + + throw new IllegalArgumentException("Integer is out of range: " + number); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/LongConverter.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/LongConverter.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/LongConverter.java new file mode 100644 index 0000000..ab2a770 --- /dev/null +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/LongConverter.java @@ -0,0 +1,58 @@ +package org.apache.cayenne.crypto.transformer.value; + +/** + * Converts between long and byte[] using big-endian encoding. + * + * @since 4.0 + */ +public class LongConverter implements BytesConverter { + + public static final BytesConverter INSTANCE = new LongConverter(); + private static final int BYTES = 8; + + static long getLong(byte[] bytes) { + if (bytes.length < BYTES) { + return IntegerConverter.getInt(bytes); + } + + if (bytes.length > BYTES) { + throw new IllegalArgumentException("byte[] is too large for a single long value: " + bytes.length); + } + + return (bytes[0] & 0xFFL) << 56 + | (bytes[1] & 0xFFL) << 48 + | (bytes[2] & 0xFFL) << 40 + | (bytes[3] & 0xFFL) << 32 + | (bytes[4] & 0xFFL) << 24 + | (bytes[5] & 0xFFL) << 16 + | (bytes[6] & 0xFFL) << 8 + | (bytes[7] & 0xFFL); + } + + static byte[] getBytes(long k) { + + if (k <= Integer.MAX_VALUE) { + return IntegerConverter.getBytes((int) k); + } + + return new byte[]{ + (byte) (k >> 56), + (byte) (k >> 48), + (byte) (k >> 40), + (byte) (k >> 32), + (byte) (k >> 24), + (byte) (k >> 16), + (byte) (k >> 8), + (byte) k}; + } + + @Override + public Object fromBytes(byte[] bytes) { + return getLong(bytes); + } + + @Override + public byte[] toBytes(Object value) { + return getBytes(((Number) value).longValue()); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ShortConverter.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ShortConverter.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ShortConverter.java new file mode 100644 index 0000000..7df6dcf --- /dev/null +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ShortConverter.java @@ -0,0 +1,68 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.cayenne.crypto.transformer.value; + +/** + * @since 4.0 + */ +public class ShortConverter implements BytesConverter { + + public static final BytesConverter INSTANCE = new ShortConverter(); + private static final int BYTES = 2; + + static short getShort(byte[] bytes) { + + if (bytes.length < BYTES) { + return ByteConverter.getByte(bytes); + } + + if (bytes.length > BYTES) { + throw new IllegalArgumentException("byte[] is too large for a single short value: " + bytes.length); + } + + return (short) ((bytes[0] & 0xFF) << 8 | (bytes[1] & 0xFF)); + } + + static byte[] getBytes(short k) { + + if (k <= Byte.MAX_VALUE) { + return ByteConverter.getBytes((byte) k); + } + + return new byte[]{ + (byte) (k >> 8), + (byte) k}; + } + + @Override + public Object fromBytes(byte[] bytes) { + return getShort(bytes); + } + + @Override + public byte[] toBytes(Object value) { + + long number = ((Number) value).longValue(); + if (number <= Short.MAX_VALUE && number >= Short.MIN_VALUE) { + return getBytes((short) number); + } + + throw new IllegalArgumentException("Short is out of range: " + number); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Utf8StringConverter.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Utf8StringConverter.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Utf8StringConverter.java index b9cddb7..314d122 100644 --- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Utf8StringConverter.java +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Utf8StringConverter.java @@ -23,11 +23,11 @@ import java.nio.charset.Charset; /** * @since 4.0 */ -final class Utf8StringConverter implements BytesConverter { +public final class Utf8StringConverter implements BytesConverter { static final String DEFAULT_CHARSET = "UTF-8"; - static final BytesConverter INSTANCE = new Utf8StringConverter(); + public static final BytesConverter INSTANCE = new Utf8StringConverter(); private Charset utf8; http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverter.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverter.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverter.java new file mode 100644 index 0000000..97458cc --- /dev/null +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverter.java @@ -0,0 +1,30 @@ +package org.apache.cayenne.crypto.transformer.value; + +import java.util.Date; +import java.util.Objects; + +/** + * Converts between java.util.Date and byte[], based on the long timestamp encoding. + * + * @since 4.0 + */ +public class UtilDateConverter implements BytesConverter { + + public static final BytesConverter INSTANCE = new UtilDateConverter(LongConverter.INSTANCE); + + private BytesConverter longConverter; + + public UtilDateConverter(BytesConverter longConverter) { + this.longConverter = Objects.requireNonNull(longConverter); + } + + @Override + public Object fromBytes(byte[] bytes) { + return new Date((long) longConverter.fromBytes(bytes)); + } + + @Override + public byte[] toBytes(Object value) { + return longConverter.toBytes(((Date) value).getTime()); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java index d1f3834..8703c4a 100644 --- a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java +++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java @@ -18,17 +18,18 @@ ****************************************************************/ package org.apache.cayenne.crypto; -import java.net.URL; - import org.apache.cayenne.configuration.server.ServerRuntime; import org.apache.cayenne.crypto.key.JceksKeySourceTest; import org.apache.cayenne.di.Module; import org.apache.cayenne.test.jdbc.DBHelper; import org.apache.cayenne.test.jdbc.TableHelper; +import java.net.URL; + public class Runtime_AES128_Base { protected ServerRuntime runtime; + protected TableHelper table1; protected TableHelper table2; protected void setUp(boolean compress) throws Exception { @@ -50,6 +51,10 @@ public class Runtime_AES128_Base { this.table2 = new TableHelper(dbHelper, "TABLE2").setColumns("ID", "PLAIN_BYTES", "CRYPTO_BYTES"); table2.deleteAll(); + + this.table1 = new TableHelper(dbHelper, "TABLE1").setColumns("ID", "PLAIN_STRING", "CRYPTO_STRING", + "PLAIN_INT", "CRYPTO_INT"); + table1.deleteAll(); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java ---------------------------------------------------------------------- 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 e337d65..2589243 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 @@ -18,22 +18,24 @@ ****************************************************************/ package org.apache.cayenne.crypto; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import java.sql.SQLException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import org.apache.cayenne.ObjectContext; +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.query.SelectQuery; import org.junit.Before; import org.junit.Test; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + public class Runtime_AES128_IT extends Runtime_AES128_Base { @Before @@ -58,6 +60,22 @@ public class Runtime_AES128_IT extends Runtime_AES128_Base { } @Test + public void testInsert_Numeric() throws SQLException { + + ObjectContext context = runtime.newContext(); + + Table1 t1 = context.newObject(Table1.class); + t1.setPlainInt(59); + t1.setCryptoInt(61); + + context.commitChanges(); + + Object[] data = table1.select(); + assertEquals(59, data[3]); + assertEquals(61, IntegerConverter.INSTANCE.fromBytes(CryptoUnitUtils.decrypt_AES_CBC((byte[]) data[4], runtime))); + } + + @Test public void testInsert_MultipleObjects() throws SQLException { ObjectContext context = runtime.newContext(); @@ -119,4 +137,23 @@ public class Runtime_AES128_IT extends Runtime_AES128_Base { assertArrayEquals(null, result.get(2).getCryptoBytes()); } + + @Test + public void test_SelectNumeric() throws SQLException { + + ObjectContext context = runtime.newContext(); + + Table1 t1 = context.newObject(Table1.class); + t1.setPlainInt(59); + t1.setCryptoInt(61); + + context.commitChanges(); + + List<Table1> result = SelectQuery.query(Table1.class).select(runtime.newContext()); + + assertEquals(1, result.size()); + assertEquals(59, result.get(0).getPlainInt()); + assertEquals(61, result.get(0).getCryptoInt()); + } + } http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/db/auto/_Table1.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/db/auto/_Table1.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/db/auto/_Table1.java index 85c5aca..97116c4 100644 --- a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/db/auto/_Table1.java +++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/db/auto/_Table1.java @@ -15,9 +15,19 @@ public abstract class _Table1 extends CayenneDataObject { public static final String ID_PK_COLUMN = "ID"; + public static final Property<Integer> CRYPTO_INT = new Property<Integer>("cryptoInt"); public static final Property<String> CRYPTO_STRING = new Property<String>("cryptoString"); + public static final Property<Integer> PLAIN_INT = new Property<Integer>("plainInt"); public static final Property<String> PLAIN_STRING = new Property<String>("plainString"); + public void setCryptoInt(int cryptoInt) { + writeProperty("cryptoInt", cryptoInt); + } + public int getCryptoInt() { + Object value = readProperty("cryptoInt"); + return (value != null) ? (Integer) value : 0; + } + public void setCryptoString(String cryptoString) { writeProperty("cryptoString", cryptoString); } @@ -25,6 +35,14 @@ public abstract class _Table1 extends CayenneDataObject { return (String)readProperty("cryptoString"); } + public void setPlainInt(int plainInt) { + writeProperty("plainInt", plainInt); + } + public int getPlainInt() { + Object value = readProperty("plainInt"); + return (value != null) ? (Integer) value : 0; + } + public void setPlainString(String plainString) { writeProperty("plainString", plainString); } http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/BooleanConverterTest.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/BooleanConverterTest.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/BooleanConverterTest.java new file mode 100644 index 0000000..ff554cb --- /dev/null +++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/BooleanConverterTest.java @@ -0,0 +1,28 @@ +package org.apache.cayenne.crypto.transformer.value; + +import org.junit.Test; + +import java.text.ParseException; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class BooleanConverterTest { + + @Test + public void testFromBytes() { + assertEquals(true, BooleanConverter.INSTANCE.fromBytes(new byte[]{1})); + assertEquals(false, BooleanConverter.INSTANCE.fromBytes(new byte[]{0})); + } + + @Test(expected = IllegalArgumentException.class) + public void testFromBytes_InvalidArray() { + BooleanConverter.INSTANCE.fromBytes(new byte[]{1, 0}); + } + + @Test + public void testToBytes() throws ParseException { + assertArrayEquals(new byte[]{0}, BooleanConverter.INSTANCE.toBytes(false)); + assertArrayEquals(new byte[]{1}, BooleanConverter.INSTANCE.toBytes(true)); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ByteConverterTest.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ByteConverterTest.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ByteConverterTest.java new file mode 100644 index 0000000..ae1ebc7 --- /dev/null +++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ByteConverterTest.java @@ -0,0 +1,45 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.cayenne.crypto.transformer.value; + + +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class ByteConverterTest { + + @Test + public void testFromBytes() { + assertEquals(new Byte((byte) 6), ByteConverter.INSTANCE.fromBytes(new byte[]{6})); + assertEquals(new Byte((byte) -7), ByteConverter.INSTANCE.fromBytes(new byte[]{-7})); + } + + @Test(expected = IllegalArgumentException.class) + public void testFromBytes_TooLong() { + ByteConverter.INSTANCE.fromBytes(new byte[]{6, 5, 4}); + } + + @Test + public void testToBytes() { + assertArrayEquals(new byte[]{6}, ByteConverter.INSTANCE.toBytes((byte) 6)); + assertArrayEquals(new byte[]{-7}, ByteConverter.INSTANCE.toBytes((byte) -7)); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/IntegerConverterTest.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/IntegerConverterTest.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/IntegerConverterTest.java new file mode 100644 index 0000000..ddc1472 --- /dev/null +++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/IntegerConverterTest.java @@ -0,0 +1,61 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.cayenne.crypto.transformer.value; + +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + + +public class IntegerConverterTest { + + @Test + public void testFromBytes_InByteRange() { + assertEquals(new Integer(6), new IntegerConverter().fromBytes(new byte[]{6})); + } + + @Test + public void testFromBytes_InShortRange() { + assertEquals(new Integer(1542), new IntegerConverter().fromBytes(new byte[]{6, 6})); + } + + @Test + public void testFromBytes_InIntRange() { + // 16 + 256*7 + 256*256* 6 + assertEquals(new Integer(395024), new IntegerConverter().fromBytes(new byte[]{0, 6, 7, 16})); + } + + @Test(expected = IllegalArgumentException.class) + public void testFromBytes_TooLong() { + IntegerConverter.INSTANCE.fromBytes(new byte[]{6, 5, 4}); + } + + @Test + public void testToBytes() { + assertArrayEquals(new byte[]{127, -1, -1, -2}, new IntegerConverter().toBytes(Integer.MAX_VALUE - 1)); + assertArrayEquals(new byte[]{127, -2}, new IntegerConverter().toBytes(Short.MAX_VALUE - 1)); + assertArrayEquals(new byte[]{-7}, new IntegerConverter().toBytes(-7)); + } + + @Test(expected = IllegalArgumentException.class) + public void testToBytesOverflow() { + new IntegerConverter().toBytes(new Long(Integer.MAX_VALUE + 55l)); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/LongConverterTest.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/LongConverterTest.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/LongConverterTest.java new file mode 100644 index 0000000..f35b5bf --- /dev/null +++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/LongConverterTest.java @@ -0,0 +1,49 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.cayenne.crypto.transformer.value; + +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + + +public class LongConverterTest { + + + @Test + public void testFromBytes_InIntRange() { + // 256*256* 6 + 256*7 + 16 + assertEquals(new Long(395024), new LongConverter().fromBytes(new byte[]{0, 6, 7, 16})); + } + + @Test + public void testFromBytes_InLongRange() { + // 6*256*256*256*256 + 7*256*256*256 + 16*256*256 + 17*256 + 99 + assertEquals(new Long(25888297315l), new LongConverter().fromBytes(new byte[]{0, 0, 0, 6, 7, 16, 17, 99})); + } + + @Test + public void testToBytes() { + assertArrayEquals(new byte[]{127, -1, -1, -1, -1, -1, -1, -2}, new LongConverter().toBytes((long) (Long.MAX_VALUE - 1l))); + assertArrayEquals(new byte[]{127, -1, -1, -2}, new LongConverter().toBytes((long) (Integer.MAX_VALUE - 1))); + assertArrayEquals(new byte[]{127, -2}, new LongConverter().toBytes(Short.MAX_VALUE - 1)); + assertArrayEquals(new byte[]{-7}, new LongConverter().toBytes(-7)); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ShortConverterTest.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ShortConverterTest.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ShortConverterTest.java new file mode 100644 index 0000000..50088c9 --- /dev/null +++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ShortConverterTest.java @@ -0,0 +1,48 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.cayenne.crypto.transformer.value; + +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class ShortConverterTest { + + @Test + public void testFromBytes_InByteRange() { + assertEquals(new Short((short) 6), ShortConverter.INSTANCE.fromBytes(new byte[]{6})); + } + + @Test + public void testFromBytes_InShortRange() { + assertEquals(new Short((short) 1287), ShortConverter.INSTANCE.fromBytes(new byte[]{5, 7})); + } + + @Test(expected = IllegalArgumentException.class) + public void testFromBytes_TooLong() { + ShortConverter.INSTANCE.fromBytes(new byte[]{6, 5, 4}); + } + + @Test + public void testToBytes() { + assertArrayEquals(new byte[]{127, -2}, ShortConverter.INSTANCE.toBytes((short) (Short.MAX_VALUE - 1))); + assertArrayEquals(new byte[]{-7}, ShortConverter.INSTANCE.toBytes((short) -7)); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverterTest.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverterTest.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverterTest.java new file mode 100644 index 0000000..a2691ae --- /dev/null +++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverterTest.java @@ -0,0 +1,31 @@ +package org.apache.cayenne.crypto.transformer.value; + +import org.junit.Test; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class UtilDateConverterTest { + + private Date date(String dateString) throws ParseException { + SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + f.setTimeZone(TimeZone.getTimeZone("GMT")); + return f.parse(dateString); + } + + @Test + public void testFromBytes() throws ParseException { + assertEquals(date("2015-01-07 11:00:02"), UtilDateConverter.INSTANCE.fromBytes(new byte[]{0, 0, 1, 74, -60, 13, 31, 80})); + } + + @Test + public void testToBytes() throws ParseException { + assertArrayEquals(new byte[]{0, 0, 1, 74, -60, 13, 31, 80}, + UtilDateConverter.INSTANCE.toBytes(date("2015-01-07 11:00:02"))); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java index 0695042..0f3e4d0 100644 --- a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java +++ b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java @@ -18,22 +18,22 @@ ****************************************************************/ package org.apache.cayenne.crypto.unit; +import org.apache.cayenne.configuration.server.ServerRuntime; +import org.apache.cayenne.crypto.key.KeySource; +import org.apache.cayenne.crypto.transformer.bytes.Header; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.nio.ByteBuffer; import java.security.Key; import java.util.Arrays; import java.util.Random; import java.util.zip.GZIPInputStream; -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; - -import org.apache.cayenne.configuration.server.ServerRuntime; -import org.apache.cayenne.crypto.key.KeySource; -import org.apache.cayenne.crypto.transformer.bytes.Header; - public class CryptoUnitUtils { public static byte[] bytesOfSize(int len) { @@ -97,4 +97,25 @@ public class CryptoUnitUtils { } } + public static byte[] toByteArray(int integer) { + + if (integer <= Short.MAX_VALUE) { + return toByteArray((short) integer); + } + + return ByteBuffer.allocate(4).putInt(integer).array(); + } + + public static byte[] toByteArray(short shortInt) { + if (shortInt <= Byte.MAX_VALUE) { + return toByteArray((byte) shortInt); + } + + return ByteBuffer.allocate(2).putShort(shortInt).array(); + } + + public static byte[] toByteArray(byte byteInt) { + return new byte[]{byteInt}; + } + } http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/resources/datamap.map.xml ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/test/resources/datamap.map.xml b/cayenne-crypto/src/test/resources/datamap.map.xml index 2a20be2..2ea6a80 100644 --- a/cayenne-crypto/src/test/resources/datamap.map.xml +++ b/cayenne-crypto/src/test/resources/datamap.map.xml @@ -5,8 +5,10 @@ project-version="8"> <property name="defaultPackage" value="org.apache.cayenne.crypto.db"/> <db-entity name="TABLE1"> + <db-attribute name="CRYPTO_INT" type="BLOB"/> <db-attribute name="CRYPTO_STRING" type="CLOB"/> <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/> + <db-attribute name="PLAIN_INT" type="INTEGER"/> <db-attribute name="PLAIN_STRING" type="VARCHAR" length="200"/> </db-entity> <db-entity name="TABLE2"> @@ -19,7 +21,9 @@ <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/> </db-entity> <obj-entity name="Table1" className="org.apache.cayenne.crypto.db.Table1" dbEntityName="TABLE1"> + <obj-attribute name="cryptoInt" type="int" db-attribute-path="CRYPTO_INT"/> <obj-attribute name="cryptoString" type="java.lang.String" db-attribute-path="CRYPTO_STRING"/> + <obj-attribute name="plainInt" type="int" db-attribute-path="PLAIN_INT"/> <obj-attribute name="plainString" type="java.lang.String" db-attribute-path="PLAIN_STRING"/> </obj-entity> <obj-entity name="Table2" className="org.apache.cayenne.crypto.db.Table2" dbEntityName="TABLE2"> http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-server/src/main/java/org/apache/cayenne/dba/TypesMapping.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/TypesMapping.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/TypesMapping.java index 161d2c1..1eed22a 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/dba/TypesMapping.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/TypesMapping.java @@ -19,6 +19,21 @@ package org.apache.cayenne.dba; +import org.apache.cayenne.util.Util; + +import java.io.Serializable; +import java.math.BigInteger; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import static java.sql.Types.ARRAY; import static java.sql.Types.BIGINT; import static java.sql.Types.BINARY; @@ -50,20 +65,6 @@ import static java.sql.Types.TINYINT; import static java.sql.Types.VARBINARY; import static java.sql.Types.VARCHAR; -import java.io.Serializable; -import java.math.BigInteger; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.cayenne.util.Util; - /** * A utility class that handles mappings of JDBC data types to the database * types and Java types. Also contains methods that provide information about @@ -280,6 +281,30 @@ public class TypesMapping { return JdbcAdapter.supportsLength(type); } + + // TODO: redo all isXyz as an internal enum over types, where each enum object knows whether it is this or that kind + + /** + * Returns true if supplied type is a character type. + * @since 4.0 + * @param type JDBC type + * @return true if supplied type is a character type. + */ + public static boolean isCharacter(int type) { + return type == Types.CHAR || type == Types.NCHAR || type == Types.VARCHAR || type == Types.NVARCHAR + || type == Types.CLOB || type == Types.NCLOB || type == Types.LONGVARCHAR || type == Types.LONGNVARCHAR; + } + + /** + * Returns true if supplied type is a binary type. + * @since 4.0 + * @param type JDBC type + * @return true if supplied type is a binary type. + */ + public static boolean isBinary(int type) { + return type == Types.BINARY || type == Types.BLOB || type == Types.VARBINARY || type == Types.LONGVARBINARY; + } + /** * Returns true if supplied type is a numeric type. */ http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/docs/doc/src/main/resources/RELEASE-NOTES.txt ---------------------------------------------------------------------- diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt index 8889bbb..b8054b3 100644 --- a/docs/doc/src/main/resources/RELEASE-NOTES.txt +++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt @@ -26,6 +26,7 @@ CAY-2083 Implement Protostuff as serialization service for Cayenne ROP CAY-2090 Untangle HttpRemoteService from ServiceContext thread local setup CAY-2100 Add supporting generated keys for PostgreSQL CAY-2102 EJBQL: db: path not supported in select columns +CAY-2103 cayenne-crypto: support for mapping non-String and non-binary types Bug Fixes: