Files
spring-boot-demo/redis/src/test/java/com/ankurm/redis/JacksonSerializerTest.java
T

110 lines
5.0 KiB
Java

package com.ankurm.redis;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import com.ankurm.redis.model.User;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJacksonJsonRedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* The "after" picture: String keys and Jackson 3 values. Writes docs/output/06-jackson-serializers.txt
* and 07-jackson-untrusted-class.txt.
*/
@RedisTest
class JacksonSerializerTest {
@Autowired
@Qualifier("userRedisTemplate")
RedisTemplate<String, User> userTemplate;
@Autowired
@Qualifier("jsonRedisTemplate")
RedisTemplate<String, Object> jsonTemplate;
@Autowired
RedisConnectionFactory factory;
@BeforeEach
void clean() {
LocalRedis.flush();
}
@Test
void typedAndGenericSerializers() {
try (Transcript t = new Transcript("06-jackson-serializers.txt",
"Jackson 3 serializers: JacksonJsonRedisSerializer and GenericJacksonJsonRedisSerializer")) {
t.section("JacksonJsonRedisSerializer<User>: one type, plain JSON");
t.line("value serializer: %s", userTemplate.getValueSerializer().getClass().getName());
userTemplate.opsForValue().set("user:1", new User(1, "Ankur"));
t.line("userTemplate.opsForValue().set(\"user:1\", new User(1, \"Ankur\"))");
t.cli("KEYS", "*");
t.cli("GET", "user:1");
User typed = userTemplate.opsForValue().get("user:1");
t.line("read back: %s (%s)", typed, typed.getClass().getSimpleName());
assertThat(typed).isEqualTo(new User(1, "Ankur"));
t.section("GenericJacksonJsonRedisSerializer with an allow-list: any type, plus @class");
t.line("value serializer: %s", jsonTemplate.getValueSerializer().getClass().getName());
jsonTemplate.opsForValue().set("user:2", new User(2, "Ankur"));
t.line("jsonTemplate.opsForValue().set(\"user:2\", new User(2, \"Ankur\"))");
t.cli("GET", "user:2");
Object generic = jsonTemplate.opsForValue().get("user:2");
t.line("read back: %s (%s)", generic, generic.getClass().getSimpleName());
assertThat(generic).isEqualTo(new User(2, "Ankur"));
t.section("GenericJacksonJsonRedisSerializer with no typing configured");
RedisTemplate<String, Object> plain = new RedisTemplate<>();
plain.setConnectionFactory(factory);
plain.setKeySerializer(StringRedisSerializer.UTF_8);
plain.setValueSerializer(GenericJacksonJsonRedisSerializer.builder().build());
plain.afterPropertiesSet();
plain.opsForValue().set("user:3", new User(3, "Ankur"));
t.line("plain.opsForValue().set(\"user:3\", new User(3, \"Ankur\"))");
t.cli("GET", "user:3");
Object untyped = plain.opsForValue().get("user:3");
t.line("read back: %s (%s)", untyped, untyped.getClass().getSimpleName());
t.section("Two different shapes of the same JSON");
t.cli("GET", "user:1");
t.cli("GET", "user:2");
}
}
@Test
void whoeverWritesToRedisChoosesTheClass() {
try (Transcript t = new Transcript("07-jackson-untrusted-class.txt",
"The @class property is input: the allow-list decides what it may name")) {
t.section("A class outside the allow-list");
t.cli("SET", "user:evil", "{\"@class\":\"com.ankurm.other.Outsider\",\"name\":\"x\"}");
t.line("jsonTemplate.opsForValue().get(\"user:evil\")");
Throwable denied = catchThrowable(() -> jsonTemplate.opsForValue().get("user:evil"));
assertThat(denied).isInstanceOf(SerializationException.class);
t.line("%s", denied.getClass().getName());
t.line(" message: %s", firstLine(denied.getMessage()));
t.section("A class inside the allow-list that no longer exists");
t.cli("SET", "user:gone", "{\"@class\":\"com.ankurm.redis.model.Gone\",\"id\":9}");
t.line("jsonTemplate.opsForValue().get(\"user:gone\")");
Throwable gone = catchThrowable(() -> jsonTemplate.opsForValue().get("user:gone"));
assertThat(gone).isInstanceOf(SerializationException.class);
t.line("%s", gone.getClass().getName());
t.line(" message: %s", firstLine(gone.getMessage()));
}
}
private static String firstLine(String message) {
return message == null ? "null" : message.lines().findFirst().orElse("");
}
}