WIP JSonDeserializerWithAccessFilter
This commit is contained in:
parent
0b7ebac472
commit
998a5a8aa1
@ -0,0 +1,34 @@
|
|||||||
|
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 static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked;
|
||||||
|
|
||||||
|
public class JSonDeserializerWithAccessFilter<T> {
|
||||||
|
|
||||||
|
private final T dto;
|
||||||
|
private final TreeNode treeNode;
|
||||||
|
|
||||||
|
public JSonDeserializerWithAccessFilter(final JsonParser jsonParser, final DeserializationContext deserializationContext, Class<T> dtoClass) {
|
||||||
|
this.treeNode = unchecked(() -> jsonParser.getCodec().readTree(jsonParser));
|
||||||
|
this.dto = unchecked(() -> dtoClass.newInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,19 @@
|
|||||||
package org.hostsharing.hsadminng.service.dto;
|
package org.hostsharing.hsadminng.service.dto;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
import com.fasterxml.jackson.core.TreeNode;
|
import com.fasterxml.jackson.core.TreeNode;
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
|
||||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
|
||||||
import com.fasterxml.jackson.databind.node.IntNode;
|
import com.fasterxml.jackson.databind.node.IntNode;
|
||||||
import com.fasterxml.jackson.databind.node.TextNode;
|
import com.fasterxml.jackson.databind.node.TextNode;
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
|
||||||
import org.hostsharing.hsadminng.security.SecurityUtils;
|
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.AccessFor;
|
import org.hostsharing.hsadminng.service.accessfilter.AccessFor;
|
||||||
|
import org.hostsharing.hsadminng.service.accessfilter.JSonDeserializerWithAccessFilter;
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||||
import org.springframework.boot.jackson.JsonComponent;
|
import org.springframework.boot.jackson.JsonComponent;
|
||||||
|
|
||||||
import javax.validation.constraints.*;
|
import javax.validation.constraints.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.annotation.*;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,23 +174,10 @@ public class CustomerDTO implements Serializable {
|
|||||||
public static class UserJsonDeserializer extends JsonDeserializer<CustomerDTO> {
|
public static class UserJsonDeserializer extends JsonDeserializer<CustomerDTO> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CustomerDTO deserialize(JsonParser jsonParser,
|
public CustomerDTO deserialize(final JsonParser jsonParser,
|
||||||
DeserializationContext deserializationContext) throws IOException {
|
final DeserializationContext deserializationContext) throws IOException {
|
||||||
|
|
||||||
TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
|
return new JSonDeserializerWithAccessFilter<CustomerDTO>(jsonParser, deserializationContext, CustomerDTO.class).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());
|
|
||||||
|
|
||||||
return dto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package org.hostsharing.hsadminng.service.util;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ReflectionUtil {
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ThrowingSupplier<T> {
|
||||||
|
T get() throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T unchecked(final ThrowingSupplier<T> supplier) {
|
||||||
|
try {
|
||||||
|
return supplier.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
package org.hostsharing.hsadminng.service.accessfilter;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
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.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
public class JSonDeserializerWithAccessFilterUnitTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public MockitoRule mockitoRule = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
public JsonGenerator jsonGenerator;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldDeserializeStringField() throws IOException {
|
||||||
|
// given
|
||||||
|
final String givenJSon = asJSon(ImmutablePair.of("stringField", "String Value"));
|
||||||
|
|
||||||
|
// when
|
||||||
|
new JSonDeserializerWithAccessFilter<C>().deserialize(givenJSon, jsonGenerator, null);
|
||||||
|
|
||||||
|
// then
|
||||||
|
verify(jsonGenerator).writeStringField("openStringField", givenDTO.openStringField);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldSerializeRestrictedFieldIfRequiredRoleIsCoveredByUser() throws IOException {
|
||||||
|
|
||||||
|
// given
|
||||||
|
givenLoginUserWithRole(Role.FINANCIAL_CONTACT);
|
||||||
|
|
||||||
|
// when
|
||||||
|
new JSonSerializerWithAccessFilter().serialize(givenDTO, jsonGenerator, null);
|
||||||
|
|
||||||
|
// then
|
||||||
|
verify(jsonGenerator).writeStringField("restrictedField", givenDTO.restrictedField);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldNotSerializeRestrictedFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
|
||||||
|
|
||||||
|
// given
|
||||||
|
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||||
|
|
||||||
|
// when
|
||||||
|
new JSonSerializerWithAccessFilter().serialize(givenDTO, jsonGenerator, null);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- fixture code below ---
|
||||||
|
|
||||||
|
private String asJSon(final ImmutablePair<String, ? extends Object>... properties) {
|
||||||
|
final StringBuilder json = new StringBuilder();
|
||||||
|
for ( ImmutablePair<String, ? extends Object> prop: properties ) {
|
||||||
|
json.append(inQuotes(prop.left));
|
||||||
|
json.append(": ");
|
||||||
|
if ( prop.right instanceof Number ) {
|
||||||
|
json.append(prop.right);
|
||||||
|
} else {
|
||||||
|
json.append(inQuotes(prop.right));
|
||||||
|
}
|
||||||
|
json.append(",\n");
|
||||||
|
}
|
||||||
|
return "{\n" + json.substring(0, json.length()-2) + "\n}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String inQuotes(Object value) {
|
||||||
|
return "\"" + value.toString() + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GivenDto {
|
||||||
|
@AccessFor(update = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||||
|
String restrictedField;
|
||||||
|
|
||||||
|
@AccessFor(update = Role.ANYBODY)
|
||||||
|
String openStringField;
|
||||||
|
|
||||||
|
@AccessFor(update = Role.ANYBODY)
|
||||||
|
Integer openIntegerField;
|
||||||
|
|
||||||
|
@AccessFor(update = Role.ANYBODY)
|
||||||
|
Long openLongField;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user