I am currently working on a project and have to deal with some test cases. We implementented cryptographie in our database to learn how it is build but now we have to update the tests. The crypto should be hidden or someway.
This is the converter we used and added #Convert in the entity classes like so
#Column(name ="name", nullable = false)
#Convert(converter = DBEncryptedStringConverter.class)
public EncryptedString getName() {
return this.name;
}
public void setName(EncryptedString name) {
this.name = name;
}
#Converter
#Slf4j
public class DBEncryptedStringConverter implements AttributeConverter<EncryptedString, String> {
private static final String ENC = "ENC__##__", RAW = "RAW__##__";
#Override
public String convertToDatabaseColumn(EncryptedString attribute) {
if(attribute == null) return null;
return (attribute.isEncrypted() ? ENC : RAW) + attribute.getString();
}
#Override
public EncryptedString convertToEntityAttribute(String dbData) {
if(dbData == null) return null;
if(dbData.startsWith(ENC)) {
return new EncryptedString(dbData.substring(ENC.length()), true);
} else if(dbData.startsWith(RAW)) {
return new EncryptedString(dbData.substring(RAW.length()), false);
} else {
log.warn("DB Entry without prefix found");
log.warn("Treating as RAW");
return new EncryptedString(dbData, false);
}
}
}
I would like to use this class for testing
#Converter
public class MockDBEncryptedStringConverter implements AttributeConverter<EncryptedString, String> {
#Override
public String convertToDatabaseColumn(EncryptedString attribute) {
return attribute.getString();
}
#Override
public EncryptedString convertToEntityAttribute(String dbData) {
return new EncryptedString(dbData, false);
}
}
I think mokcing with mokito would be a way to go, but I dont quiet understand how to use it
Related
I understand there may be cleaner ways to store these data, I'm skipping that part for the sake of my sanity in dealing with legacy code.
I want to store an object that looks like this in DynamoDB:
#DynamoDBTable(tableName="TableName")
public class MyItem {
// DynamoDB Attributes
private String hashKey;
private String someAttribute;
private Map<String, Config> configs;
#DynamoDBHashKey(attributeName = "hash_key")
public String getHashKey() {
return this.hashKey;
}
public void setHashKey(String hashKey) {
this.hashKey = hashKey;
}
#DynamoDBAttribute(attributeName = "some_attribute")
public String getSomeAttribute() {
return this.someAttribute;
}
public void setSomeAttribute(String someAttribute ) {
this.someAttribute = someAttribute;
}
#DynamoDBAttribute(attributeName = "configs")
public Map<String, Config> getConfigs() {
return this.configs;
}
public void setConfigs(Map<String, Config> configs)
{
this.configs = configs;
}
}
With a supplemental class
#DynamoDBDocument
public class Config {
private String field;
#DynamoDBAttribute(attributeName="field")
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
}
Will this work as written?
What would the resulting entry look like in DynamoDB for the following JSON:
{
"hash_key":"123",
"some_attribute":"attribute_value",
"a_config_key" : {
"field":"field_value"
}
}
I would recommend you to implement your own converter using #DynamoDbConvertedBy (the official dynamodb-enhanced client).
Hopefully, this sample is helpful: https://stackoverflow.com/a/70602166/12869305
I have a toy DynamoDB table with a hash key, and in it I want to store some simple items in DynamoDB document format.
Here's the class to represent the document:
#DynamoDBDocument
public class Simple {
private String key;
private String value;
public Simple() {
}
public Simple(
String key,
String value
) {
this.key = key;
this.value = value;
}
#DynamoDBAttribute
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
#DynamoDBAttribute
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
and here's a class to represent the entry in the table:
#DynamoDBDocument
public class SimpleRow {
private String hash;
private Simple entry;
public SimpleRow() {
}
public SimpleRow(
String hash,
Simple entry
) {
this.hash = hash;
this.entry = entry;
}
#DynamoDBHashKey
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
#DynamoDBAttribute
public Simple getEntry() {
return entry;
}
public void setEntry(Simple entry) {
this.entry = entry;
}
}
With this setup, I can save entries to and laod entries from the table without a problem, using a DynamoDBMapper:
DynamoDBMapper mapper = new DynamoDBMapper(
AmazonDynamoDBClientBuilder.standard().build(),
DynamoDBMapperConfig
.builder()
.withTableNameOverride(new DynamoDBMapperConfig.TableNameOverride("simple"))
.build()
);
mapper.save(new SimpleRow("the-hash", new Simple("foo", "bar")));
System.out.println(mapper.load(SimpleRow.class, "the-hash"));
which results in an entry looking like this:
{
"entry": {
"M": {
"key": {
"S": "foo"
},
"value": {
"S": "bar"
}
}
},
"hash": {
"S": "the-key"
}
}
However, I dislike the pollution of the Simple class with setters and zero-argument constructor - it should be an immutable data class. Therefore, I'd like to use a DynamoDBTypeConverter to construct the Simple objects.
The problem is I can't seem to write a converter that works, as I can't find a suitable source type:
Map, which would seem the most natural, fails with DynamoDBMappingException: not supported; requires #DynamoDBTyped or #DynamoDBTypeConverted
String, which would seem to be a reasonable default, doesn't error but the string supplied to the convert() method is always empty
Object, which would seem to be a catch-all, isn't supported by Dynamo and fails with DynamoDBMappingException: could not resolve type of class SimpleConverter
Is it possible to write a DynamoDBTypeConverter for this type? If so, what type should I use? I've trawled the internet for exampels of this but nothing seems forthcoming.
You need to change Simple to Simple[] in SimleRow.java
import java.util.Arrays;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBDocument;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
#DynamoDBDocument
public class SimpleRow {
private String hash;
private Simple[] entry;
public SimpleRow() {
}
public SimpleRow(
String hash,
Simple[] entry
) {
this.hash = hash;
this.entry = entry;
}
#DynamoDBHashKey
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
#DynamoDBAttribute
public Simple[] getEntry() {
return entry;
}
public void setEntry(Simple[] entry) {
this.entry = entry;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("SimpleRow [hash=");
builder.append(hash);
builder.append(", entry=");
builder.append(Arrays.toString(entry));
builder.append("]");
return builder.toString();
}
}
And call it in main class as new SimpleRow("the-hash", new Simple[] {new Simple("foo", "bar")})
mapper.save(new SimpleRow("the-hash", new Simple[] {new Simple("foo", "bar")}));
Scenario: A data object which persists in the DB table. There are some old entries in the table. Now I have to apply encryption to new further entries in the table. So I add a new column which has the field encrypted set to False by default to check if the values are encrypted.
Problem: I want to write an annotation to encrypt the fields in the data model(POJO) before persisting and decrypt on getter() calls only if it is encrypted.
Context:
The user model.
public class UserData {
#Id
#Column(name = "ID", length = 36)
private String id;
#Column(name = "IS_ENCRYPTED")
private boolean isEncrypted;
#Column(name = "NAME")
#Convert(converter = EncryptionConverter.class)
private String name;
// more fields ....
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// more similar getter and setters
}
The encryption class that i have written.
#Converter
public class EncryptionConverter implements AttributeConverter<String, String>{
private final String secretKey= "someSecret";
UserData Data = new UserData();
#Override
public String convertToDatabaseColumn(String str) {
if(!isNullOrBlank(str))
return AesEncrypt.encrypt(str, secretKey);
return str;
}
#Override
public String convertToEntityAttribute(String encrypedStr) {
if(!isNullOrBlank(encrypedStr) && Data.isEncrypted)
return AesEncrypt.decrypt(encrypedStr, secretKey);
return encrypedStr;
}
}
This class is inside the model class. (can move outside, but how to pass isencrypted flag to annotation)
How can I do this, is my approach correct?
Edit: there are multiple fields which are to be encrypted/decrypted not just name.
You can create the encryption behaviour in another configuration class, say EncryptedPropertyConfig, in this you can create a bean, EncryptablePropertyResolver from jasypt-spring-boot
#EnableAutoConfiguration
public class EncryptedPropertyConfig {
public EncryptedPropertyConfig() {
}
#Bean
public EncryptablePropertyResolver encryptablePropertyResolver() {
EncryptablePropertyResolver r = new MyPropertyPlaceholderConfigurer();
return r;
}
}
public final class MyPropertyPlaceholderConfigurer implements EncryptablePropertyResolver {
private StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
private EnvironmentStringPBEConfig envConfig = new EnvironmentStringPBEConfig();
public MyPropertyPlaceholderConfigurer() {
// set the encryption key and config
}
public String resolvePropertyValue(String passedValue) {
if (!PropertyValueEncryptionUtils.isEncryptedValue(passedValue)) {
return passedValue;
} else {
String returnValue = "";
try {
returnValue = PropertyValueEncryptionUtils.decrypt(passedValue, this.encryptor);
return returnValue;
} catch (Exception var4) {
throw new RuntimeException("Error in decryption of property value:" + passedValue, var4);
}
}
}
}
I suggest alternative solution using Entity Listeners
import javax.persistence.PostLoad;
import javax.persistence.PreUpdate;
public class UserData {
private final String secretKey= "someSecret";
// ...
#PreUpdate
private void onUpdate() {
// triggered before saving entity to DB (both create & update)
if(!isNullOrBlank(name)) {
name = AesEncrypt.encrypt(name, secretKey);
}
}
#PostLoad
private void onLoad() {
// triggered after entity is fetched from Entity Provider
if (!isNullOrBlank(name) && isEncrypted) {
name = AesEncrypt.decrypt(name, secretKey);
}
}
}
Instead of using JPA AttributeConverter you can implement hibernate user type in this way:
import java.util.Objects;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.StringType;
import org.hibernate.usertype.UserType;
public class CustomNameType implements UserType
{
private String secretKey = "someSecret";
public CustomNameType()
{
}
#Override
public Object deepCopy(Object value) throws HibernateException
{
if (null == value) return null;
return ((CustomName) value).clone();
}
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return cached;
}
#Override
public Serializable disassemble(Object value) throws HibernateException
{
return (Serializable) value;
}
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
#Override
public boolean equals(Object one, Object two) throws HibernateException
{
return Objects.equals(one, two);
}
#Override
public int hashCode(Object obj) throws HibernateException
{
return Objects.hashCode(obj);
}
#Override
public boolean isMutable()
{
return true;
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException
{
boolean isEncrypted = rs.getBoolean(0); // IS_ENCRYPTED
String name = rs.getString(1); // NAME
if (isEncrypted) {
name = AesEncrypt.decrypt(name, secretKey);
}
return new CustomName(isEncrypted, name);
}
#Override
public void nullSafeSet(PreparedStatement statement, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException
{
CustomName customName = (CustomName) value;
String name = customName.getName();
if (customName.isEncrypted()) {
name = AesEncrypt.encrypt(name, secretKey);
}
statement.setBoolean(0, customName.isEncrypted());
statement.setString(1, name);
}
#Override
public Class<?> returnedClass()
{
return CustomName.class;
}
#Override
public int[] sqlTypes()
{
// I do not know the types of your IS_ENCRYPTED and NAME fields
// So, this place maybe require correction
int[] types = {BooleanType.INSTANCE.sqlType(), StringType.INSTANCE.sqlType()};
return types;
}
}
where CustomName is:
public class CustomName implements Serializable, Cloneable
{
private boolean isEncrypted;
private String name;
public CustomName(boolean isEncrypted, String name)
{
this.isEncrypted = isEncrypted;
this.name = name;
}
// getters , equals, hashCode ...
#Override
public CustomName clone()
{
return new CustomName(isEncrypted, name);
}
}
and then use it:
import org.hibernate.annotations.Type;
import org.hibernate.annotations.Columns;
#Entity
public class UserData {
#Type(type = "com.your.CustomNameType")
#Columns(columns = {
#Column(name = "IS_ENCRYPTED"),
#Column(name = "NAME")
})
private CustomName name;
}
I am very novice to Java programming. I am C# developer. I was working on one of the small project where we want to use the redis cache instead of ehcache.
We are using spring.data.redis version 2.2.3.RELEASE and redis.client.jedis version 3.2.0.
Here is my redisconf class which contains RedisTemplate as well:
public class RedisConf {
private String REDIS_HOSTNAME = "localhost";
private int REDIS_PORT = 6379;
protected JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(REDIS_HOSTNAME, REDIS_PORT);
JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder().usePooling().build();
JedisConnectionFactory factory = new JedisConnectionFactory(configuration,jedisClientConfiguration);
factory.afterPropertiesSet();
return factory;
}
public RedisTemplate<String,Element> redisTemplate() {
final RedisTemplate<String,Element> redisTemplate = new RedisTemplate<String,Element>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new GenericToStringSerializer<Object>(Object.class));
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setConnectionFactory(jedisConnectionFactory());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
Here is how instantiate RedisConf class:
RedisConf rf = new RedisConf();
RedisTemplate<String, Element> redisTemplate = rf.redisTemplate();
redisTemplate.getConnectionFactory().getConnection().ping();
cache = new RedisDelegate(redisTemplate);
Here is my RedisDeligate class where I do all my get and put operation:
public class RedisDelegate implements Cache {
private final RedisTemplate<String,Element> redisTemplate;
private final Logger LOGGER = LoggerFactory.getLogger(RedisDelegate.class);
private static String REDIS_KEY = "Redis";
public RedisDelegate(RedisTemplate<String,Element> redisTemplate) {
this.redisTemplate = redisTemplate;
}
#Override
public String getName() {
return "my-redis";
}
#Override
public Object getNativeCache() {
return this.redisTemplate;
}
#Override
public Element get(Object key) {
try {
LOGGER.debug("Key is: {}", key);
Object element = this.redisTemplate.opsForHash().get(REDIS_KEY, key);
LOGGER.debug("element Object is: {}", element);
return (element == null) ? null : (Element) element;
//return (Element) this.redisTemplate.opsForHash().get(REDIS_KEY, key);
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
return null;
}
#Override
public void put(Element element) {
this.redisTemplate.opsForHash().put(REDIS_KEY, element.key(), element);
}
#Override
public void evict(Object key) {
this.redisTemplate.opsForHash().delete(REDIS_KEY, key);
}
#Override
public void clear() {
redisTemplate.execute((RedisCallback<Object>) connection -> {
connection.flushDb();
return null;
});
}
}
Here is CacheElement Object that I am trying to put in the cache which implements Serializable as seen below:
public class CacheElement implements Element, Serializable {
.
.
.
}
When I try to put an object into cache, it fails with an error that serialization failed. I know there is some problem with my RedisTemplate but I can't seen to figure out. Here is the error I am receiving:
Cannot serialize; nested exception is
org.springframework.core.serializer.support.SerializationFailedException:
Failed to serialize object using DefaultSerializer; nested exception
is java.lang.IllegalArgumentException: DefaultSerializer requires a
Serializable payload but received an object of type
[io.gravitee.policy.cache.resource.CacheElement]
EDIT
I am trying to store the instance of CacheElement class into Redis:
public class CacheElement implements Element {
private final String key;
private final CacheResponse response;
private int timeToLive = 0;
public CacheElement(String key, CacheResponse response) {
this.key = key;
this.response = response;
}
public int getTimeToLive() {
return timeToLive;
}
public void setTimeToLive(int timeToLive) {
this.timeToLive = timeToLive;
}
#Override
public Object key() {
return key;
}
#Override
public Object value() {
return response;
}
#Override
public int timeToLive() {
return timeToLive;
}
}
CacheResponse object contains Buffer.
public class CacheResponse {
private int status;
private HttpHeaders headers;
private Buffer content;
public Buffer getContent() {
return content;
}
public void setContent(Buffer content) {
this.content = content;
}
public HttpHeaders getHeaders() {
return headers;
}
public void setHeaders(HttpHeaders headers) {
this.headers = headers;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
}
I would like to serialize this CacheElement object and store it in Redis.
I also would like to deserialize it on get operation.
I will appreciate help fixing this issue. As I mentioned, I am not a Java developer. I am coming from C# and visual studio world.
Thanks
Please implement your paypload object class with Serializable interface
Mapping an enum class in to DynamoDB object is really simple by using Custom Marshall. But how to map a List of Enum?
Enum class
public enum Transport {
SMS,EMAIL,ALL;
}
DynamoDB mapper
public class Campaign{
private List<Transport> transport;
#DynamoDBAttribute(attributeName = "transport")
public List<Transport> getTransport() {
return transport;
}
public void setTransport(List<Transport> transport) {
this.transport = transport;
}
}
DynamoDBMarshaller is deprecated.
Use DynamoDBTypeConverter instead.
Example:
Enum class
public static enum ValidationFailure {
FRAUD, GENERAL_ERROR
}
DynamoDBTable class
#DynamoDBTable(tableName = "receipt")
public class Receipt {
private Long id;
private List<ValidationFailure> validation;
#DynamoDBHashKey(attributeName = "id")
public Long getId() {
return id;
}
#DynamoDBTypeConverted(converter = ValidationConverter.class)
public List<ValidationFailure> getValidation() {
return validation;
}
public void setId(Long id) {
this.id = id;
}
public void setValidation(List<ValidationFailure> validation) {
this.validation = validation;
}
}
Convertor:
public class ValidationConverter implements DynamoDBTypeConverter<List<String>, List<ValidationFailure>> {
#Override
public List<String> convert(List<ValidationFailure> object) {
List<String> result = new ArrayList<String>();
if (object != null) {
object.stream().forEach(e -> result.add(e.name()));
}
return result;
}
#Override
public List<ValidationFailure> unconvert(List<String> object) {
List<ValidationFailure> result = new ArrayList<ValidationFailure>();
if (object != null) {
object.stream().forEach(e -> result.add(ValidationFailure.valueOf(e)));
}
return result;
}
}
It's working for me, I have used the Set
#DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.SS)
var roles: MutableSet<Employee.Role>? = null
I think the same approach would work for List with DynamoDBAttributeType.L
I found the answer myself. I create a custom marshall like below.
public class TransportMarshaller implements DynamoDBMarshaller<List<Transport>> {
#Override
public String marshall(List<Transport> transports) {
List<String>transportMap=new ArrayList<>();
for(Transport transport:transports){
transportMap.add(transport.name());
}
return transportMap.toString().replaceAll("\\[|\\]", "");//Save as comma separate value for the purpose of easiness to unmarshall
}
#Override
public List<Transport> unmarshall(Class<List<Transport>> aClass, String s) {
List<String>map= Arrays.asList(s.split("\\s*,\\s*")); //split from comma and parse to List
List<Transport>transports=new ArrayList<>();
for (String st:map){
transports.add(Transport.valueOf(st));
}
return transports;
}
}