1
0

JSonDeserializerWithAccessFilter

This commit is contained in:
Michael Hoennig
2019-04-23 06:17:55 +02:00
parent 998a5a8aa1
commit 1dae396d99
7 changed files with 130 additions and 62 deletions

View File

@ -3,6 +3,13 @@ package org.hostsharing.hsadminng.service.accessfilter;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.LongNode;
import com.fasterxml.jackson.databind.node.TextNode;
import org.apache.commons.lang3.NotImplementedException;
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
import java.lang.reflect.Field;
import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked;
@ -17,18 +24,40 @@ public class JSonDeserializerWithAccessFilter<T> {
}
public T deserialize() {
//
// CustomerDTO dto = new CustomerDTO();
// dto.setId(((IntNode) treeNode.get("id")).asLong());
// dto.setReference(((IntNode) treeNode.get("reference")).asInt());
// dto.setPrefix(((TextNode) treeNode.get("prefix")).asText());
// dto.setName(((TextNode) treeNode.get("name")).asText());
// dto.setContractualAddress(((TextNode) treeNode.get("contractualAddress")).asText());
// dto.setContractualSalutation(((TextNode) treeNode.get("contractualSalutation")).asText());
// dto.setBillingAddress(((TextNode) treeNode.get("billingAddress")).asText());
// dto.setBillingSalutation(((TextNode) treeNode.get("billingSalutation")).asText());
// dto.setRemark(((TextNode) treeNode.get("remark")).asText());
treeNode.fieldNames().forEachRemaining(fieldName -> {
try {
final Field field = dto.getClass().getDeclaredField(fieldName);
final Object value = readValue(treeNode, field);
writeValue(dto, field, value);
} catch (NoSuchFieldException e) {
throw new RuntimeException("setting field " + fieldName + " failed", e);
}
});
return dto;
}
private Object readValue(final TreeNode treeNode, final Field field) {
final TreeNode fieldNode = treeNode.get(field.getName());
if (fieldNode instanceof TextNode) {
return ((TextNode)fieldNode).asText();
} else if (fieldNode instanceof IntNode) {
return ((IntNode)fieldNode).asInt();
} else if (fieldNode instanceof LongNode) {
return ((LongNode)fieldNode).asLong();
} else {
throw new NotImplementedException("property type not yet implemented: " + field);
}
}
private void writeValue(final T dto, final Field field, final Object value) {
if ( field.getType().isAssignableFrom(value.getClass()) ) {
ReflectionUtil.setValue(dto, field, value);
} else if (Integer.class.isAssignableFrom(field.getType()) || int.class.isAssignableFrom(field.getType())) {
ReflectionUtil.setValue(dto, field, ((Number)value).intValue());
} else if (Long.class.isAssignableFrom(field.getType()) || long.class.isAssignableFrom(field.getType())) {
ReflectionUtil.setValue(dto, field, ((Number)value).longValue());
} else {
throw new NotImplementedException("property type not yet implemented: " + field);
}
}
}

View File

@ -40,7 +40,7 @@ public class JSonSerializerWithAccessFilter extends JsonSerializer<Object> {
} else if (String.class.isAssignableFrom(prop.getType())) {
jsonGenerator.writeStringField(fieldName, (String) get(dto, prop));
} else {
throw new NotImplementedException("property type not yet implemented" + prop);
throw new NotImplementedException("property type not yet implemented: " + prop);
}
}
}

View File

@ -175,9 +175,9 @@ public class CustomerDTO implements Serializable {
@Override
public CustomerDTO deserialize(final JsonParser jsonParser,
final DeserializationContext deserializationContext) throws IOException {
final DeserializationContext deserializationContext) {
return new JSonDeserializerWithAccessFilter<CustomerDTO>(jsonParser, deserializationContext, CustomerDTO.class).deserialize();
return new JSonDeserializerWithAccessFilter<>(jsonParser, deserializationContext, CustomerDTO.class).deserialize();
}
}
}

View File

@ -1,9 +1,31 @@
package org.hostsharing.hsadminng.service.util;
import com.fasterxml.jackson.core.TreeNode;
import java.lang.reflect.Field;
import java.util.function.Supplier;
public class ReflectionUtil {
public static <T> void setValue(final T dto, final String fieldName, final Object value) {
try {
final Field field = dto.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(dto, value);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static <T> void setValue(final T dto, final Field field, final Object value) {
try {
field.setAccessible(true);
field.set(dto, value);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
@FunctionalInterface
public interface ThrowingSupplier<T> {
T get() throws Exception;

View File

@ -1,8 +1,14 @@
package org.hostsharing.hsadminng.service.accessfilter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.hostsharing.hsadminng.service.dto.CustomerDTO;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@ -15,6 +21,7 @@ import java.io.IOException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenLoginUserWithRole;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@ -24,68 +31,55 @@ public class JSonDeserializerWithAccessFilterUnitTest {
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock
public JsonGenerator jsonGenerator;
public JsonParser jsonParser;
@Mock
public ObjectCodec codec;
@Mock
public TreeNode treeNode;
@Before
public void init() {
public void init() throws IOException {
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
given(jsonParser.getCodec()).willReturn(codec);
}
@Test
public void shouldDeserializeStringField() throws IOException {
// given
final String givenJSon = asJSon(ImmutablePair.of("stringField", "String Value"));
givenJSonTree(asJSon(ImmutablePair.of("openStringField", "String Value")));
// when
new JSonDeserializerWithAccessFilter<C>().deserialize(givenJSon, jsonGenerator, null);
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenDto.class).deserialize();
// then
verify(jsonGenerator).writeStringField("openStringField", givenDTO.openStringField);
assertThat(actualDto.openStringField).isEqualTo("String Value");
}
@Test
public void shouldSerializeRestrictedFieldIfRequiredRoleIsCoveredByUser() throws IOException {
public void shouldDeserializeIntegerField() throws IOException {
// given
givenLoginUserWithRole(Role.FINANCIAL_CONTACT);
givenJSonTree(asJSon(ImmutablePair.of("openIntegerField", 1234)));
// when
new JSonSerializerWithAccessFilter().serialize(givenDTO, jsonGenerator, null);
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenDto.class).deserialize();
// then
verify(jsonGenerator).writeStringField("restrictedField", givenDTO.restrictedField);
assertThat(actualDto.openIntegerField).isEqualTo(1234);
}
@Test
public void shouldNotSerializeRestrictedFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
public void shouldDeserializeLongField() throws IOException {
// given
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
givenJSonTree(asJSon(ImmutablePair.of("openLongField", 1234L)));
// when
new JSonSerializerWithAccessFilter().serialize(givenDTO, jsonGenerator, null);
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenDto.class).deserialize();
// then
verify(jsonGenerator, never()).writeStringField("restrictedField", givenDTO.restrictedField);
}
@Test
public void shouldThrowExceptionForUnimplementedFieldType() throws IOException {
// given
class Arbitrary {
}
class GivenDtoWithUnimplementedFieldType {
@AccessFor(read = Role.ANYBODY)
Arbitrary fieldWithUnimplementedType;
}
final GivenDtoWithUnimplementedFieldType givenDto = new GivenDtoWithUnimplementedFieldType();
// when
Throwable actual = catchThrowable(() -> new JSonSerializerWithAccessFilter().serialize(givenDto, jsonGenerator, null));
// then
assertThat(actual).isInstanceOf(NotImplementedException.class);
assertThat(actualDto.openLongField).isEqualTo(1234L);
}
// --- fixture code below ---
@ -105,11 +99,15 @@ public class JSonDeserializerWithAccessFilterUnitTest {
return "{\n" + json.substring(0, json.length()-2) + "\n}";
}
private void givenJSonTree(String givenJSon) throws IOException {
given(codec.readTree(jsonParser)).willReturn(new ObjectMapper().readTree(givenJSon));
}
private String inQuotes(Object value) {
return "\"" + value.toString() + "\"";
}
private static class GivenDto {
public static class GivenDto {
@AccessFor(update = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
String restrictedField;

View File

@ -36,7 +36,7 @@ public class JSonSerializerWithAccessFilterUnitTest {
@Test
public void shouldSerializeStringField() throws IOException {
// when
// when
new JSonSerializerWithAccessFilter().serialize(givenDTO, jsonGenerator, null);
// then