JSonSerializerWithAccessFilterUnitTest

This commit is contained in:
Michael Hoennig 2019-04-19 10:46:40 +02:00
parent 1ad74907bd
commit a24ca35bd7
4 changed files with 151 additions and 27 deletions

View File

@ -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);
}
}
} }

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
} }