JSonSerializerWithAccessFilterUnitTest
This commit is contained in:
parent
1ad74907bd
commit
a24ca35bd7
@ -6,7 +6,6 @@ import com.fasterxml.jackson.databind.JsonSerializer;
|
|||||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
import org.hostsharing.hsadminng.security.SecurityUtils;
|
import org.hostsharing.hsadminng.security.SecurityUtils;
|
||||||
import org.hostsharing.hsadminng.service.dto.CustomerDTO;
|
|
||||||
import org.springframework.boot.jackson.JsonComponent;
|
import org.springframework.boot.jackson.JsonComponent;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -18,20 +17,22 @@ import java.lang.reflect.Method;
|
|||||||
public class JSonSerializerWithAccessFilter extends JsonSerializer<Object> {
|
public class JSonSerializerWithAccessFilter extends JsonSerializer<Object> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(Object dto, JsonGenerator jsonGenerator,
|
public void serialize(final Object dto, final JsonGenerator jsonGenerator,
|
||||||
SerializerProvider serializerProvider) throws IOException {
|
final SerializerProvider serializerProvider) throws IOException {
|
||||||
|
|
||||||
|
// TODO: move the implementation to an (if necessary, inner) class
|
||||||
jsonGenerator.writeStartObject();
|
jsonGenerator.writeStartObject();
|
||||||
for (Field prop : CustomerDTO.class.getDeclaredFields()) {
|
for (Field prop : dto.getClass().getDeclaredFields()) {
|
||||||
toJSon(dto, jsonGenerator, prop);
|
toJSon(dto, jsonGenerator, prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonGenerator.writeEndObject();
|
jsonGenerator.writeEndObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toJSon(Object dto, JsonGenerator jsonGenerator, Field prop) throws IOException {
|
private void toJSon(final Object dto, final JsonGenerator jsonGenerator, final Field prop) throws IOException {
|
||||||
if (getLoginUserRole().isAllowedToRead(prop)) {
|
if (getLoginUserRole().isAllowedToRead(prop)) {
|
||||||
final String fieldName = prop.getName();
|
final String fieldName = prop.getName();
|
||||||
|
// TODO: maybe replace by serializerProvider.defaultSerialize...()?
|
||||||
if (Integer.class.isAssignableFrom(prop.getType()) || int.class.isAssignableFrom(prop.getType())) {
|
if (Integer.class.isAssignableFrom(prop.getType()) || int.class.isAssignableFrom(prop.getType())) {
|
||||||
jsonGenerator.writeNumberField(fieldName, (int) get(dto, prop));
|
jsonGenerator.writeNumberField(fieldName, (int) get(dto, prop));
|
||||||
} else if (Long.class.isAssignableFrom(prop.getType()) || long.class.isAssignableFrom(prop.getType())) {
|
} else if (Long.class.isAssignableFrom(prop.getType()) || long.class.isAssignableFrom(prop.getType())) {
|
||||||
@ -44,25 +45,16 @@ public class JSonSerializerWithAccessFilter extends JsonSerializer<Object> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object get(Object dto, Field field) {
|
private Object get(final Object dto, final Field field) {
|
||||||
try {
|
try {
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
return field.get(dto);
|
return field.get(dto);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException("getting field " + field + " failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Role getLoginUserRole() {
|
private Role getLoginUserRole() {
|
||||||
return SecurityUtils.getCurrentUserLogin().map(u -> Role.valueOf(u.toUpperCase())).orElse(Role.ANYBODY);
|
return SecurityUtils.getCurrentUserLogin().map(u -> Role.valueOf(u.toUpperCase())).orElse(Role.ANYBODY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object invoke(Object dto, Method method) {
|
|
||||||
try {
|
|
||||||
return method.invoke(dto);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,114 @@
|
|||||||
|
package org.hostsharing.hsadminng.service.accessfilter;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.apache.commons.lang3.RandomUtils;
|
||||||
|
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 JSonSerializerWithAccessFilterUnitTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public MockitoRule mockitoRule = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
public JsonGenerator jsonGenerator;
|
||||||
|
|
||||||
|
private final GivenDto givenDTO = createSampleDto();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldSerializeStringField() throws IOException {
|
||||||
|
// when
|
||||||
|
new JSonSerializerWithAccessFilter().serialize(givenDTO, 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 GivenDto createSampleDto() {
|
||||||
|
final GivenDto dto = new GivenDto();
|
||||||
|
dto.restrictedField = RandomStringUtils.randomAlphabetic(10);
|
||||||
|
dto.openStringField = RandomStringUtils.randomAlphabetic(10);
|
||||||
|
dto.openIntegerField = RandomUtils.nextInt();
|
||||||
|
dto.openLongField = RandomUtils.nextLong();
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GivenDto {
|
||||||
|
@AccessFor(read = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||||
|
String restrictedField;
|
||||||
|
|
||||||
|
@AccessFor(read = Role.ANYBODY)
|
||||||
|
String openStringField;
|
||||||
|
|
||||||
|
@AccessFor(read = Role.ANYBODY)
|
||||||
|
Integer openIntegerField;
|
||||||
|
|
||||||
|
@AccessFor(read = Role.ANYBODY)
|
||||||
|
Long openLongField;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package org.hostsharing.hsadminng.service.accessfilter;
|
||||||
|
|
||||||
|
import org.hostsharing.hsadminng.security.SecurityUtils;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class MockSecurityContext {
|
||||||
|
|
||||||
|
public static void givenLoginUserWithRole(final Role userRole) {
|
||||||
|
final String fakeUserName = userRole.name();
|
||||||
|
|
||||||
|
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
||||||
|
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(fakeUserName, "dummy"));
|
||||||
|
SecurityContextHolder.setContext(securityContext);
|
||||||
|
Optional<String> login = SecurityUtils.getCurrentUserLogin();
|
||||||
|
|
||||||
|
assertThat(login).describedAs("precondition failed").contains(fakeUserName);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package org.hostsharing.hsadminng.service.dto;
|
|||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.hostsharing.hsadminng.security.SecurityUtils;
|
import org.hostsharing.hsadminng.security.SecurityUtils;
|
||||||
|
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -16,6 +17,7 @@ import java.io.IOException;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenLoginUserWithRole;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
@JsonTest
|
@JsonTest
|
||||||
@ -30,7 +32,7 @@ public class CustomerDTOUnitTest {
|
|||||||
|
|
||||||
// given
|
// given
|
||||||
CustomerDTO given = createSomeCustomerDTO();
|
CustomerDTO given = createSomeCustomerDTO();
|
||||||
givenLoginUserWithRole("ANY_CUSTOMER_USER");
|
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String actual = objectMapper.writeValueAsString(given);
|
String actual = objectMapper.writeValueAsString(given);
|
||||||
@ -49,7 +51,7 @@ public class CustomerDTOUnitTest {
|
|||||||
|
|
||||||
// given
|
// given
|
||||||
CustomerDTO given = createSomeCustomerDTO();
|
CustomerDTO given = createSomeCustomerDTO();
|
||||||
givenLoginUserWithRole("SUPPORTER");
|
givenLoginUserWithRole(Role.SUPPORTER);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String actual = objectMapper.writeValueAsString(given);
|
String actual = objectMapper.writeValueAsString(given);
|
||||||
@ -62,7 +64,7 @@ public class CustomerDTOUnitTest {
|
|||||||
public void testDeserializeAsContractualCustomerContact() throws IOException {
|
public void testDeserializeAsContractualCustomerContact() throws IOException {
|
||||||
// given
|
// given
|
||||||
String json = "{\"id\":1234,\"reference\":10001,\"prefix\":\"abc\",\"name\":\"Mein Name\",\"contractualAddress\":\"Eine Adresse\",\"contractualSalutation\":\"Hallo\",\"billingAddress\":\"Noch eine Adresse\",\"billingSalutation\":\"Moin\",\"remark\":\"Eine Bemerkung\"}";
|
String json = "{\"id\":1234,\"reference\":10001,\"prefix\":\"abc\",\"name\":\"Mein Name\",\"contractualAddress\":\"Eine Adresse\",\"contractualSalutation\":\"Hallo\",\"billingAddress\":\"Noch eine Adresse\",\"billingSalutation\":\"Moin\",\"remark\":\"Eine Bemerkung\"}";
|
||||||
givenLoginUserWithRole("CONTRACTUAL_CONTACT");
|
givenLoginUserWithRole(Role.CONTRACTUAL_CONTACT);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
CustomerDTO actual = objectMapper.readValue(json, CustomerDTO.class);
|
CustomerDTO actual = objectMapper.readValue(json, CustomerDTO.class);
|
||||||
@ -120,12 +122,4 @@ public class CustomerDTOUnitTest {
|
|||||||
given.setRemark("Eine Bemerkung");
|
given.setRemark("Eine Bemerkung");
|
||||||
return given;
|
return given;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void givenLoginUserWithRole(String userName) {
|
|
||||||
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
|
||||||
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(userName, userName));
|
|
||||||
SecurityContextHolder.setContext(securityContext);
|
|
||||||
Optional<String> login = SecurityUtils.getCurrentUserLogin();
|
|
||||||
assertThat(login).describedAs("precondition failed").contains(userName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user