JSonDeserializerWithAccessFilter
This commit is contained in:
parent
998a5a8aa1
commit
1dae396d99
41
package-lock.json
generated
41
package-lock.json
generated
@ -6025,7 +6025,8 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -6046,12 +6047,14 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -6066,17 +6069,20 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -6193,7 +6199,8 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -6205,6 +6212,7 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -6219,6 +6227,7 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -6226,12 +6235,14 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -6250,6 +6261,7 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -6330,7 +6342,8 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -6342,6 +6355,7 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -6427,7 +6441,8 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -6463,6 +6478,7 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -6482,6 +6498,7 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -6525,12 +6542,14 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user