Deserializer: improved test code coverage for IGNORE field access
This commit is contained in:
parent
f9b68df901
commit
e7cb3622f3
@ -82,13 +82,16 @@ abstract class JSonAccessFilter<T extends AccessMappings> {
|
|||||||
|
|
||||||
final Class<?> parentDtoClass = ReflectionUtil.<T> determineGenericInterfaceParameter(parentDtoLoader, rawType, 0);
|
final Class<?> parentDtoClass = ReflectionUtil.<T> determineGenericInterfaceParameter(parentDtoLoader, rawType, 0);
|
||||||
final Long parentId = ReflectionUtil.getValue(dto, parentIdField);
|
final Long parentId = ReflectionUtil.getValue(dto, parentIdField);
|
||||||
|
if (parentId == null) {
|
||||||
|
return emptySet();
|
||||||
|
}
|
||||||
final Set<Role> rolesOnParent = getLoginUserDirectRolesFor(parentDtoClass, parentId);
|
final Set<Role> rolesOnParent = getLoginUserDirectRolesFor(parentDtoClass, parentId);
|
||||||
|
|
||||||
final Object parentEntity = loadDto(parentDtoLoader, parentId);
|
final Object parentEntity = loadDto(parentDtoLoader, parentId);
|
||||||
return union(rolesOnParent, getLoginUserRoleOnAncestorIfHigher(parentEntity));
|
return union(rolesOnParent, getLoginUserRoleOnAncestorIfHigher(parentEntity));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Role> getLoginUserDirectRolesFor(final Class<?> dtoClass, final Long id) {
|
private Set<Role> getLoginUserDirectRolesFor(final Class<?> dtoClass, final long id) {
|
||||||
if (!SecurityUtils.isAuthenticated()) {
|
if (!SecurityUtils.isAuthenticated()) {
|
||||||
return emptySet();
|
return emptySet();
|
||||||
}
|
}
|
||||||
|
@ -199,38 +199,47 @@ public abstract class JsonDeserializerWithAccessFilter<T extends AccessMappings>
|
|||||||
private void checkAccessToWrittenFields(final T currentDto) {
|
private void checkAccessToWrittenFields(final T currentDto) {
|
||||||
updatingFields.forEach(
|
updatingFields.forEach(
|
||||||
field -> {
|
field -> {
|
||||||
// TODO this ugly code needs cleanup
|
|
||||||
if (!field.equals(selfIdField)) {
|
if (!field.equals(selfIdField)) {
|
||||||
final Set<Role> roles = getLoginUserRoles();
|
final Set<Role> roles = getLoginUserRoles();
|
||||||
if (isInitAccess()) {
|
if (isInitAccess()) {
|
||||||
if (!isAllowedToInit(roles, field)) {
|
validateInitAccess(field, roles);
|
||||||
if (!field.equals(parentIdField)) {
|
} else {
|
||||||
throw new BadRequestAlertException(
|
validateUpdateAccess(field, roles);
|
||||||
"Initialization of field " + toDisplay(field)
|
|
||||||
+ " prohibited for current user role(s): "
|
|
||||||
+ Joiner.on("+").join(roles),
|
|
||||||
toDisplay(field),
|
|
||||||
"initializationProhibited");
|
|
||||||
} else {
|
|
||||||
throw new BadRequestAlertException(
|
|
||||||
"Referencing field " + toDisplay(field)
|
|
||||||
+ " prohibited for current user role(s): "
|
|
||||||
+ Joiner.on("+").join(roles),
|
|
||||||
toDisplay(field),
|
|
||||||
"referencingProhibited");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!Role.toBeIgnoredForUpdates(field) && !isAllowedToUpdate(getLoginUserRoles(), field)) {
|
|
||||||
throw new BadRequestAlertException(
|
|
||||||
"Update of field " + toDisplay(field) + " prohibited for current user role(s): "
|
|
||||||
+ Joiner.on("+").join(roles),
|
|
||||||
toDisplay(field),
|
|
||||||
"updateProhibited");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateInitAccess(Field field, Set<Role> roles) {
|
||||||
|
if (!Role.toBeIgnoredForUpdates(field) && !isAllowedToInit(roles, field)) {
|
||||||
|
if (!field.equals(parentIdField)) {
|
||||||
|
throw new BadRequestAlertException(
|
||||||
|
"Initialization of field " + toDisplay(field)
|
||||||
|
+ " prohibited for current user role(s): "
|
||||||
|
+ Joiner.on("+").join(roles),
|
||||||
|
toDisplay(field),
|
||||||
|
"initializationProhibited");
|
||||||
|
} else {
|
||||||
|
throw new BadRequestAlertException(
|
||||||
|
"Referencing field " + toDisplay(field)
|
||||||
|
+ " prohibited for current user role(s): "
|
||||||
|
+ Joiner.on("+").join(roles),
|
||||||
|
toDisplay(field),
|
||||||
|
"referencingProhibited");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateUpdateAccess(Field field, Set<Role> roles) {
|
||||||
|
if (!Role.toBeIgnoredForUpdates(field) && !isAllowedToUpdate(getLoginUserRoles(), field)) {
|
||||||
|
throw new BadRequestAlertException(
|
||||||
|
"Update of field " + toDisplay(field) + " prohibited for current user role(s): "
|
||||||
|
+ Joiner.on("+").join(roles),
|
||||||
|
toDisplay(field),
|
||||||
|
"updateProhibited");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isAllowedToInit(final Set<Role> roles, final Field field) {
|
private boolean isAllowedToInit(final Set<Role> roles, final Field field) {
|
||||||
for (Role role : roles) {
|
for (Role role : roles) {
|
||||||
if (role.isAllowedToInit(field)) {
|
if (role.isAllowedToInit(field)) {
|
||||||
|
@ -106,6 +106,9 @@ public class JSonAccessFilterTestFixture {
|
|||||||
|
|
||||||
@AccessFor(init = ANYBODY, update = ANYBODY, read = ANYBODY)
|
@AccessFor(init = ANYBODY, update = ANYBODY, read = ANYBODY)
|
||||||
int[] openArrayField;
|
int[] openArrayField;
|
||||||
|
|
||||||
|
@AccessFor(init = IGNORED, update = IGNORED, read = ANYBODY)
|
||||||
|
String displayLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
static abstract class GivenService implements IdToDtoResolver<GivenDto> {
|
static abstract class GivenService implements IdToDtoResolver<GivenDto> {
|
||||||
|
@ -119,7 +119,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("openStringField", null)));
|
ImmutablePair.of("openStringField", null)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.openStringField).isNull();
|
assertThat(actualDto.openStringField).isNull();
|
||||||
@ -135,7 +135,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("openStringField", "String Value")));
|
ImmutablePair.of("openStringField", "String Value")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.openStringField).isEqualTo("String Value");
|
assertThat(actualDto.openStringField).isEqualTo("String Value");
|
||||||
@ -152,7 +152,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);;
|
final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null);;
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -170,7 +170,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedBigDecimalField", SOME_BIG_DECIMAL_WITH_ANOTHER_SCALE)));
|
ImmutablePair.of("restrictedBigDecimalField", SOME_BIG_DECIMAL_WITH_ANOTHER_SCALE)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null);
|
||||||
;
|
;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -201,7 +201,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("openEnumField", TestEnum.GREEN)));
|
ImmutablePair.of("openEnumField", TestEnum.GREEN)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null);
|
||||||
;
|
;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -227,7 +227,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("openArrayField", Arrays.asList(11, 22, 33))));
|
ImmutablePair.of("openArrayField", Arrays.asList(11, 22, 33))));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Throwable exception = catchThrowable(() -> deserializerForGivenDto().deserialize(jsonParser, null));
|
Throwable exception = catchThrowable(() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOf(NotImplementedException.class);
|
assertThat(exception).isInstanceOf(NotImplementedException.class);
|
||||||
@ -245,7 +245,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedField", "update value of restricted field")));
|
ImmutablePair.of("restrictedField", "update value of restricted field")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.restrictedField).isEqualTo("update value of restricted field");
|
assertThat(actualDto.restrictedField).isEqualTo("update value of restricted field");
|
||||||
@ -263,7 +263,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedField", "initial value of restricted field")));
|
ImmutablePair.of("restrictedField", "initial value of restricted field")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
final GivenDto actualDto = deserializerFor(GivenDto.class).deserialize(jsonParser, null);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.restrictedField).isEqualTo("initial value of restricted field");
|
assertThat(actualDto.restrictedField).isEqualTo("initial value of restricted field");
|
||||||
@ -280,7 +280,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedField", "updated value of restricted field")));
|
ImmutablePair.of("restrictedField", "updated value of restricted field")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final Throwable exception = catchThrowable(() -> deserializerForGivenDto().deserialize(jsonParser, null));
|
final Throwable exception = catchThrowable(() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||||
@ -300,7 +300,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedField", "another value of restricted field")));
|
ImmutablePair.of("restrictedField", "another value of restricted field")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final Throwable exception = catchThrowable(() -> deserializerForGivenDto().deserialize(jsonParser, null));
|
final Throwable exception = catchThrowable(() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||||
@ -320,7 +320,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
Throwable exception = catchThrowable(
|
Throwable exception = catchThrowable(
|
||||||
() -> deserializerForGivenChildDto().deserialize(jsonParser, null));
|
() -> deserializerFor(GivenChildDto.class).deserialize(jsonParser, null));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||||
@ -339,7 +339,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("parentId", 1234L)));
|
ImmutablePair.of("parentId", 1234L)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final GivenChildDto actualDto = deserializerForGivenChildDto().deserialize(jsonParser, null);
|
final GivenChildDto actualDto = deserializerFor(GivenChildDto.class).deserialize(jsonParser, null);
|
||||||
;
|
;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -359,7 +359,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
final Throwable exception = catchThrowable(
|
final Throwable exception = catchThrowable(
|
||||||
() -> deserializerForGivenDto().deserialize(jsonParser, null));
|
() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||||
@ -375,7 +375,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
final Throwable exception = catchThrowable(
|
final Throwable exception = catchThrowable(
|
||||||
() -> deserializerForGivenDtoWithMultipleSelfId().deserialize(jsonParser, null));
|
() -> deserializerFor(GivenDtoWithMultipleSelfId.class).deserialize(jsonParser, null));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOf(AssertionError.class)
|
assertThat(exception).isInstanceOf(AssertionError.class)
|
||||||
@ -390,7 +390,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
final Throwable exception = catchThrowable(
|
final Throwable exception = catchThrowable(
|
||||||
() -> deserializerForGivenDtoWithUnknownFieldType().deserialize(jsonParser, null));
|
() -> deserializerFor(GivenDtoWithUnknownFieldType.class).deserialize(jsonParser, null));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOf(NotImplementedException.class)
|
assertThat(exception).isInstanceOf(NotImplementedException.class)
|
||||||
@ -407,7 +407,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
final Throwable exception = catchThrowable(
|
final Throwable exception = catchThrowable(
|
||||||
() -> deserializerForGivenDto().deserialize(jsonParser, null));
|
() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, (exc) -> {
|
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, (exc) -> {
|
||||||
@ -417,6 +417,38 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldIgnorePropertyToIgnoreForInit() throws IOException {
|
||||||
|
// given
|
||||||
|
securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN);
|
||||||
|
givenJSonTree(
|
||||||
|
asJSon(
|
||||||
|
ImmutablePair.of("displayLabel", "Some Value")));
|
||||||
|
|
||||||
|
// when
|
||||||
|
deserializerFor(GivenDto.class).deserialize(jsonParser, null);
|
||||||
|
final Throwable exception = catchThrowable(() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(exception).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldIgnorePropertyToIgnoreForUpdate() throws IOException {
|
||||||
|
// given
|
||||||
|
securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN);
|
||||||
|
givenJSonTree(
|
||||||
|
asJSon(
|
||||||
|
ImmutablePair.of("id", 1234L),
|
||||||
|
ImmutablePair.of("displayLabel", "Some Value")));
|
||||||
|
|
||||||
|
// when
|
||||||
|
final Throwable exception = catchThrowable(() -> deserializerFor(GivenDto.class).deserialize(jsonParser, null));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(exception).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
// --- only fixture code below ---
|
// --- only fixture code below ---
|
||||||
|
|
||||||
private void givenJSonTree(String givenJSon) throws IOException {
|
private void givenJSonTree(String givenJSon) throws IOException {
|
||||||
@ -425,28 +457,41 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
given(codec.readTree(jsonParser)).willReturn(new ObjectMapper().readTree(givenJSon));
|
given(codec.readTree(jsonParser)).willReturn(new ObjectMapper().readTree(givenJSon));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need specialied factories for the deserializer subclasses so that the generic type can be accessed via reflection.
|
// We need specialied factories for the deserializer subclasses
|
||||||
|
// so that the generic type can be accessed via reflection.
|
||||||
// And it's down here to keep the ugly formatting out of the test cases.
|
// And it's down here to keep the ugly formatting out of the test cases.
|
||||||
|
// The trick with the unused ...-parameterr might look strange but the reason is
|
||||||
|
// that I wanted the concrete class to be navigable in the tests and
|
||||||
|
// multiple deserializer(Class<...Dto>) methods would have the same erasure
|
||||||
|
// and adding the type the method name, is redundant for the reader.
|
||||||
|
|
||||||
public JsonDeserializerWithAccessFilter<GivenDto> deserializerForGivenDto() throws IOException {
|
public JsonDeserializerWithAccessFilter<GivenDto> deserializerFor(
|
||||||
|
final Class<GivenDto> clazz,
|
||||||
|
final GivenDto... qualifier) {
|
||||||
return new JsonDeserializerWithAccessFilter<GivenDto>(ctx, userRoleAssignmentService) {
|
return new JsonDeserializerWithAccessFilter<GivenDto>(ctx, userRoleAssignmentService) {
|
||||||
// no need to overload any method here
|
// no need to overload any method here
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonDeserializerWithAccessFilter<GivenChildDto> deserializerForGivenChildDto() throws IOException {
|
public JsonDeserializerWithAccessFilter<GivenChildDto> deserializerFor(
|
||||||
|
final Class<GivenChildDto> clazz,
|
||||||
|
final GivenChildDto... qualifier) {
|
||||||
return new JsonDeserializerWithAccessFilter<GivenChildDto>(ctx, userRoleAssignmentService) {
|
return new JsonDeserializerWithAccessFilter<GivenChildDto>(ctx, userRoleAssignmentService) {
|
||||||
// no need to overload any method here
|
// no need to overload any method here
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsonDeserializer<GivenDtoWithMultipleSelfId> deserializerForGivenDtoWithMultipleSelfId() {
|
private JsonDeserializer<GivenDtoWithMultipleSelfId> deserializerFor(
|
||||||
|
final Class<GivenDtoWithMultipleSelfId> clazz,
|
||||||
|
final GivenDtoWithMultipleSelfId... qualifier) {
|
||||||
return new JsonDeserializerWithAccessFilter<GivenDtoWithMultipleSelfId>(ctx, userRoleAssignmentService) {
|
return new JsonDeserializerWithAccessFilter<GivenDtoWithMultipleSelfId>(ctx, userRoleAssignmentService) {
|
||||||
// no need to overload any method here
|
// no need to overload any method here
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsonDeserializer<GivenDtoWithUnknownFieldType> deserializerForGivenDtoWithUnknownFieldType() {
|
private JsonDeserializer<GivenDtoWithUnknownFieldType> deserializerFor(
|
||||||
|
final Class<GivenDtoWithUnknownFieldType> clazz,
|
||||||
|
final GivenDtoWithUnknownFieldType... qualifier) {
|
||||||
return new JsonDeserializerWithAccessFilter<GivenDtoWithUnknownFieldType>(ctx, userRoleAssignmentService) {
|
return new JsonDeserializerWithAccessFilter<GivenDtoWithUnknownFieldType>(ctx, userRoleAssignmentService) {
|
||||||
// no need to overload any method here
|
// no need to overload any method here
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user