JSonSerializer/DeserializerWithAccessFilter: also use role in parent
This commit is contained in:
parent
1505e7bd66
commit
a94516b3ce
@ -1,9 +1,12 @@
|
||||
package org.hostsharing.hsadminng.security;
|
||||
|
||||
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@ -11,6 +14,8 @@ import java.util.Optional;
|
||||
*/
|
||||
public final class SecurityUtils {
|
||||
|
||||
private static List<UserRoleAssignment> userRoleAssignments = new ArrayList<>();
|
||||
|
||||
private SecurityUtils() {
|
||||
}
|
||||
|
||||
@ -73,4 +78,42 @@ public final class SecurityUtils {
|
||||
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority)))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
public static Role getLoginUserRoleFor(final Class<?> onDtoClass, final Long onId) {
|
||||
|
||||
final Role highestRole = userRoleAssignments.stream().
|
||||
map(ura ->
|
||||
matches(onDtoClass, onId, ura)
|
||||
? ura.role
|
||||
: Role.ANYBODY).
|
||||
reduce(Role.ANYBODY, (r1, r2) -> r1.covers(r2) ? r1 : r2);
|
||||
return highestRole;
|
||||
}
|
||||
|
||||
private static boolean matches(Class<?> onDtoClass, Long onId, UserRoleAssignment ura) {
|
||||
final boolean matches = (ura.onClass == null || onDtoClass == ura.onClass) && (ura.onId == null || onId.equals(ura.onId) );
|
||||
return matches;
|
||||
}
|
||||
|
||||
// TODO: depends on https://plan.hostsharing.net/project/hsadmin/us/67?milestone=34
|
||||
public static void addUserRole(final Class<?> onClass, final Long onId, final Role role) {
|
||||
userRoleAssignments.add(new UserRoleAssignment(onClass, onId, role));
|
||||
|
||||
}
|
||||
|
||||
public static void clearUserRoles() {
|
||||
userRoleAssignments.clear();
|
||||
}
|
||||
|
||||
private static class UserRoleAssignment {
|
||||
final Class<?> onClass;
|
||||
final Long onId;
|
||||
final Role role;
|
||||
|
||||
UserRoleAssignment(Class<?> onClass, Long onId, Role role) {
|
||||
this.onClass = onClass;
|
||||
this.onId = onId;
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
package org.hostsharing.hsadminng.service.accessfilter;
|
||||
|
||||
import org.hostsharing.hsadminng.security.SecurityUtils;
|
||||
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
abstract class JSonAccessFilter<T> {
|
||||
final T dto;
|
||||
Field selfIdField = null;
|
||||
Field parentIdField = null;
|
||||
|
||||
JSonAccessFilter(final T dto) {
|
||||
this.dto = dto;
|
||||
determineIdFields();
|
||||
}
|
||||
|
||||
void determineIdFields() {
|
||||
for (Field field : dto.getClass().getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(SelfId.class)) {
|
||||
if (selfIdField != null) {
|
||||
throw new AssertionError("multiple @" + SelfId.class.getSimpleName() + " detected in " + field.getDeclaringClass().getSimpleName());
|
||||
}
|
||||
selfIdField = field;
|
||||
}
|
||||
if (field.isAnnotationPresent(ParentId.class)) {
|
||||
if (parentIdField != null) {
|
||||
throw new AssertionError("multiple @" + ParentId.class.getSimpleName() + " detected in " + field.getDeclaringClass().getSimpleName());
|
||||
}
|
||||
parentIdField = field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Long getId() {
|
||||
if (selfIdField == null) {
|
||||
return null;
|
||||
}
|
||||
return (Long) ReflectionUtil.getValue(dto, selfIdField);
|
||||
}
|
||||
|
||||
String toDisplay(final Field field) {
|
||||
return field.getDeclaringClass().getSimpleName() + "." + field.getName();
|
||||
}
|
||||
|
||||
Role getLoginUserRole() {
|
||||
final Role roleOnSelf = getLoginUserRoleOnSelf();
|
||||
final Role roleOnParent = getLoginUserRoleOnParent();
|
||||
return roleOnSelf.covers(roleOnParent) ? roleOnSelf : roleOnParent;
|
||||
}
|
||||
|
||||
|
||||
private Role getLoginUserRoleOnSelf() {
|
||||
// TODO: find broadest role in self and recursively in parent
|
||||
return SecurityUtils.getLoginUserRoleFor(dto.getClass(), getId() );
|
||||
}
|
||||
|
||||
private Role getLoginUserRoleOnParent() {
|
||||
if ( parentIdField == null ) {
|
||||
return Role.ANYBODY;
|
||||
}
|
||||
final ParentId parentId = parentIdField.getAnnotation(ParentId.class);
|
||||
return SecurityUtils.getLoginUserRoleFor(parentId.value(), (Long) ReflectionUtil.getValue(dto, parentIdField) );
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ 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.security.SecurityUtils;
|
||||
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
||||
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||
|
||||
@ -17,37 +16,23 @@ import java.util.Set;
|
||||
|
||||
import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked;
|
||||
|
||||
public class JSonDeserializerWithAccessFilter<T> {
|
||||
public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
|
||||
private final T dto;
|
||||
private final TreeNode treeNode;
|
||||
private final Set<Field> modifiedFields = new HashSet<>();
|
||||
private Field selfIdField = null;
|
||||
|
||||
public JSonDeserializerWithAccessFilter(final JsonParser jsonParser, final DeserializationContext deserializationContext, Class<T> dtoClass) {
|
||||
super(unchecked(dtoClass::newInstance));
|
||||
this.treeNode = unchecked(() -> jsonParser.getCodec().readTree(jsonParser));
|
||||
this.dto = unchecked(dtoClass::newInstance);
|
||||
}
|
||||
|
||||
// Jackson deserializes from the JsonParser, thus no input parameter needed.
|
||||
public T deserialize() {
|
||||
determineSelfIdField();
|
||||
deserializeValues();
|
||||
checkAccessToModifiedFields();
|
||||
return dto;
|
||||
}
|
||||
|
||||
private void determineSelfIdField() {
|
||||
for (Field field : dto.getClass().getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(SelfId.class)) {
|
||||
if (selfIdField != null) {
|
||||
throw new AssertionError("multiple @" + SelfId.class.getSimpleName() + " detected in " + field.getDeclaringClass().getSimpleName());
|
||||
}
|
||||
selfIdField = field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void deserializeValues() {
|
||||
treeNode.fieldNames().forEachRemaining(fieldName -> {
|
||||
try {
|
||||
@ -90,34 +75,21 @@ public class JSonDeserializerWithAccessFilter<T> {
|
||||
modifiedFields.add(field);
|
||||
}
|
||||
|
||||
private Object getId() {
|
||||
if (selfIdField == null) {
|
||||
return null;
|
||||
}
|
||||
return ReflectionUtil.getValue(dto, selfIdField);
|
||||
}
|
||||
|
||||
private void checkAccessToModifiedFields() {
|
||||
modifiedFields.forEach(field -> {
|
||||
if ( !field.equals(selfIdField) ) {
|
||||
if (getId() == null) {
|
||||
if (!getLoginUserRole().isAllowedToInit(field)) {
|
||||
if ( !field.equals(parentIdField)) {
|
||||
throw new BadRequestAlertException("Initialization of field prohibited for current user", toDisplay(field), "initializationProhibited");
|
||||
} else {
|
||||
throw new BadRequestAlertException("Referencing field prohibited for current user", toDisplay(field), "referencingProhibited");
|
||||
}
|
||||
} else if (getId() != null) {
|
||||
if (!getLoginUserRole().isAllowedToUpdate(field)) {
|
||||
}
|
||||
} else if (!getLoginUserRole().isAllowedToUpdate(field)) {
|
||||
throw new BadRequestAlertException("Update of field prohibited for current user", toDisplay(field), "updateProhibited");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String toDisplay(final Field field) {
|
||||
return field.getDeclaringClass().getSimpleName() + "." + field.getName();
|
||||
}
|
||||
|
||||
private Role getLoginUserRole() {
|
||||
return SecurityUtils.getCurrentUserLogin().map(u -> Role.valueOf(u.toUpperCase())).orElse(Role.ANYBODY);
|
||||
}
|
||||
}
|
||||
|
@ -2,28 +2,22 @@ package org.hostsharing.hsadminng.service.accessfilter;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.hostsharing.hsadminng.security.SecurityUtils;
|
||||
import org.springframework.boot.jackson.JsonComponent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class JSonSerializerWithAccessFilter <T> {
|
||||
public class JSonSerializerWithAccessFilter <T> extends JSonAccessFilter<T> {
|
||||
private final JsonGenerator jsonGenerator;
|
||||
private final SerializerProvider serializerProvider;
|
||||
private final T dto;
|
||||
|
||||
public JSonSerializerWithAccessFilter(final JsonGenerator jsonGenerator,
|
||||
final SerializerProvider serializerProvider,
|
||||
final T dto) {
|
||||
super(dto);
|
||||
this.jsonGenerator = jsonGenerator;
|
||||
this.serializerProvider = serializerProvider;
|
||||
this.dto = dto;
|
||||
}
|
||||
|
||||
// Jackson serializes into the JsonGenerator, thus no return value needed.
|
||||
@ -65,7 +59,4 @@ public class JSonSerializerWithAccessFilter <T> {
|
||||
}
|
||||
}
|
||||
|
||||
private Role getLoginUserRole() {
|
||||
return SecurityUtils.getCurrentUserLogin().map(u -> Role.valueOf(u.toUpperCase())).orElse(Role.ANYBODY);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
package org.hostsharing.hsadminng.service.accessfilter;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Used to mark a field within a DTO as containing the id of a referenced entity,
|
||||
* it's needed to determine access rights for entity creation.
|
||||
*
|
||||
* @see AccessFor
|
||||
*/
|
||||
@Documented
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ParentId {
|
||||
/// The DTO class of the referenced entity.
|
||||
Class<?> value();
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package org.hostsharing.hsadminng.service.accessfilter;
|
||||
|
||||
import org.hostsharing.hsadminng.security.SecurityUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
@ -52,7 +50,7 @@ public enum Role {
|
||||
*/
|
||||
FINANCIAL_CONTACT(22) {
|
||||
@Override
|
||||
boolean covers(final Role role) {
|
||||
public boolean covers(final Role role) {
|
||||
if (role == ACTUAL_CUSTOMER_USER) {
|
||||
return false;
|
||||
}
|
||||
@ -94,14 +92,15 @@ public enum Role {
|
||||
*
|
||||
* Where 'this' means the Java instance itself as a role of a system user.
|
||||
*
|
||||
* @example
|
||||
* {@code
|
||||
* Role.HOSTMASTER.covers(Role.ANY_CUSTOMER_USER) == true
|
||||
* }
|
||||
*
|
||||
* @param role The required role for a resource.
|
||||
*
|
||||
* @return whether this role comprises the given role
|
||||
*/
|
||||
boolean covers(final Role role) {
|
||||
public boolean covers(final Role role) {
|
||||
return this == role || this.level < role.level;
|
||||
}
|
||||
|
||||
@ -131,8 +130,6 @@ public enum Role {
|
||||
*/
|
||||
public boolean isAllowedToUpdate(final Field field) {
|
||||
|
||||
final Role loginUserRole = SecurityUtils.getCurrentUserLogin().map(u -> Role.valueOf(u.toUpperCase())).orElse(Role.ANYBODY);
|
||||
|
||||
final AccessFor accessFor = field.getAnnotation(AccessFor.class);
|
||||
if (accessFor == null) {
|
||||
return false;
|
||||
@ -150,8 +147,6 @@ public enum Role {
|
||||
*/
|
||||
public boolean isAllowedToRead(final Field field) {
|
||||
|
||||
final Role loginUserRole = SecurityUtils.getCurrentUserLogin().map(u -> Role.valueOf(u.toUpperCase())).orElse(Role.ANYBODY);
|
||||
|
||||
final AccessFor accessFor = field.getAnnotation(AccessFor.class);
|
||||
if (accessFor == null) {
|
||||
return false;
|
||||
|
@ -1,7 +1,9 @@
|
||||
package org.hostsharing.hsadminng.service.dto;
|
||||
|
||||
import org.hostsharing.hsadminng.service.accessfilter.AccessFor;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.ParentId;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.SelfId;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
@ -15,6 +17,7 @@ import java.util.function.Consumer;
|
||||
*/
|
||||
public class MembershipDTO implements Serializable {
|
||||
|
||||
@SelfId
|
||||
@AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private Long id;
|
||||
|
||||
@ -33,8 +36,8 @@ public class MembershipDTO implements Serializable {
|
||||
@AccessFor(init = Role.ADMIN, read = Role.SUPPORTER)
|
||||
private String remark;
|
||||
|
||||
// TODO @AccessFor(init = Role.ADMIN, read = Role.SUPPORTER)
|
||||
// @AccessReference(CustomerDTO.class, Role...)
|
||||
@ParentId(CustomerDTO.class)
|
||||
@AccessFor(init = Role.ADMIN, read = Role.SUPPORTER)
|
||||
private Long customerId;
|
||||
|
||||
@AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
|
@ -1,9 +1,6 @@
|
||||
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 {
|
||||
|
||||
@ -26,10 +23,10 @@ public class ReflectionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Object getValue(T dto, Field field) {
|
||||
public static <T> T getValue(final T dto, final Field field) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
return field.get(dto);
|
||||
return (T) field.get(dto);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.hostsharing.hsadminng.service.accessfilter;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeId;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.ObjectCodec;
|
||||
import com.fasterxml.jackson.core.TreeNode;
|
||||
@ -18,7 +17,8 @@ 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.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenAuthenticatedUser;
|
||||
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenUserHavingRole;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
@ -38,7 +38,8 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(GivenDto.class, 1234L, Role.ACTUAL_CUSTOMER_USER);
|
||||
|
||||
given(jsonParser.getCodec()).willReturn(codec);
|
||||
}
|
||||
@ -46,7 +47,9 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
@Test
|
||||
public void shouldDeserializeStringField() throws IOException {
|
||||
// given
|
||||
givenJSonTree(asJSon(ImmutablePair.of("openStringField", "String Value")));
|
||||
givenJSonTree(asJSon(
|
||||
ImmutablePair.of("id", 1234L),
|
||||
ImmutablePair.of("openStringField", "String Value")));
|
||||
|
||||
// when
|
||||
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenDto.class).deserialize();
|
||||
@ -58,7 +61,9 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
@Test
|
||||
public void shouldDeserializeIntegerField() throws IOException {
|
||||
// given
|
||||
givenJSonTree(asJSon(ImmutablePair.of("openIntegerField", 1234)));
|
||||
givenJSonTree(asJSon(
|
||||
ImmutablePair.of("id", 1234L),
|
||||
ImmutablePair.of("openIntegerField", 1234)));
|
||||
|
||||
// when
|
||||
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenDto.class).deserialize();
|
||||
@ -70,7 +75,9 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
@Test
|
||||
public void shouldDeserializeLongField() throws IOException {
|
||||
// given
|
||||
givenJSonTree(asJSon(ImmutablePair.of("openLongField", 1234L)));
|
||||
givenJSonTree(asJSon(
|
||||
ImmutablePair.of("id", 1234L),
|
||||
ImmutablePair.of("openLongField", 1234L)));
|
||||
|
||||
// when
|
||||
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenDto.class).deserialize();
|
||||
@ -82,8 +89,11 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
@Test
|
||||
public void shouldDeserializeStringFieldIfRequiredRoleIsCoveredByUser() throws IOException {
|
||||
// given
|
||||
givenLoginUserWithRole(Role.FINANCIAL_CONTACT);
|
||||
givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(GivenDto.class, 1234L, Role.FINANCIAL_CONTACT);
|
||||
givenJSonTree(asJSon(
|
||||
ImmutablePair.of("id", 1234L),
|
||||
ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||
|
||||
// when
|
||||
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenDto.class).deserialize();
|
||||
@ -95,7 +105,8 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
@Test
|
||||
public void shouldInitializeFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
|
||||
// given
|
||||
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(null, null, Role.ANY_CUSTOMER_USER);
|
||||
givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||
|
||||
// when
|
||||
@ -109,9 +120,41 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUpdateFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
|
||||
public void shouldNotCreateIfRoleRequiredByParentEntityIsNotCoveredByUser() throws IOException {
|
||||
// given
|
||||
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(GivenDto.class, 9999L, Role.CONTRACTUAL_CONTACT);
|
||||
givenJSonTree(asJSon(ImmutablePair.of("parentId", 1111L)));
|
||||
|
||||
// when
|
||||
Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenChildDto.class).deserialize());
|
||||
|
||||
// then
|
||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||
assertThat(badRequestAlertException.getParam()).isEqualTo("GivenChildDto.parentId");
|
||||
assertThat(badRequestAlertException.getErrorKey()).isEqualTo("referencingProhibited");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIfRoleRequiredByReferencedEntityIsCoveredByUser() throws IOException {
|
||||
// given
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(GivenDto.class, 1111L, Role.CONTRACTUAL_CONTACT);
|
||||
givenJSonTree(asJSon(ImmutablePair.of("parentId", 1111L)));
|
||||
|
||||
// when
|
||||
final GivenChildDto actualDto = new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenChildDto.class).deserialize();
|
||||
|
||||
// then
|
||||
assertThat(actualDto.parentId).isEqualTo(1111L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotUpdateFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
|
||||
// given
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(GivenDto.class, 1234L, Role.ANY_CUSTOMER_USER);
|
||||
givenJSonTree(asJSon(
|
||||
ImmutablePair.of("id", 1234L),
|
||||
ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||
@ -183,6 +226,20 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
Long openLongField;
|
||||
}
|
||||
|
||||
public static class GivenChildDto {
|
||||
|
||||
@SelfId
|
||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||
Long id;
|
||||
|
||||
@AccessFor(init = Role.CONTRACTUAL_CONTACT, update = Role.CONTRACTUAL_CONTACT, read = Role.ACTUAL_CUSTOMER_USER)
|
||||
@ParentId(GivenDto.class)
|
||||
Long parentId;
|
||||
|
||||
@AccessFor(init = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT}, update = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
String restrictedField;
|
||||
}
|
||||
|
||||
public static class GivenDtoWithMultipleSelfId {
|
||||
|
||||
@SelfId
|
||||
|
@ -15,7 +15,6 @@ 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;
|
||||
|
||||
@ -31,7 +30,8 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||
MockSecurityContext.givenAuthenticatedUser();
|
||||
MockSecurityContext.givenUserHavingRole(GivenCustomerDto.class, 888L, Role.ANY_CUSTOMER_USER);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -47,7 +47,8 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
public void shouldSerializeRestrictedFieldIfRequiredRoleIsCoveredByUser() throws IOException {
|
||||
|
||||
// given
|
||||
givenLoginUserWithRole(Role.FINANCIAL_CONTACT);
|
||||
MockSecurityContext.givenAuthenticatedUser();
|
||||
MockSecurityContext.givenUserHavingRole(GivenCustomerDto.class, 888L, Role.FINANCIAL_CONTACT);
|
||||
|
||||
// when
|
||||
new JSonSerializerWithAccessFilter<>(jsonGenerator, null, givenDTO).serialize();
|
||||
@ -60,7 +61,8 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
public void shouldNotSerializeRestrictedFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
|
||||
|
||||
// given
|
||||
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||
MockSecurityContext.givenAuthenticatedUser();
|
||||
MockSecurityContext.givenUserHavingRole(GivenCustomerDto.class, 888L, Role.ANY_CUSTOMER_USER);
|
||||
|
||||
// when
|
||||
new JSonSerializerWithAccessFilter<>(jsonGenerator, null, givenDTO).serialize();
|
||||
@ -92,6 +94,7 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
|
||||
private GivenDto createSampleDto() {
|
||||
final GivenDto dto = new GivenDto();
|
||||
dto.customerId = 888L;
|
||||
dto.restrictedField = RandomStringUtils.randomAlphabetic(10);
|
||||
dto.openStringField = RandomStringUtils.randomAlphabetic(10);
|
||||
dto.openIntegerField = RandomUtils.nextInt();
|
||||
@ -99,7 +102,15 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
return dto;
|
||||
}
|
||||
|
||||
private static class GivenCustomerDto {
|
||||
|
||||
}
|
||||
|
||||
private static class GivenDto {
|
||||
|
||||
@ParentId(GivenCustomerDto.class)
|
||||
Long customerId;
|
||||
|
||||
@AccessFor(read = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
String restrictedField;
|
||||
|
||||
|
@ -5,20 +5,20 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
|
||||
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();
|
||||
|
||||
public static void givenAuthenticatedUser() {
|
||||
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
||||
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken(fakeUserName, "dummy"));
|
||||
securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("dummyUser", "dummyPassword"));
|
||||
SecurityContextHolder.setContext(securityContext);
|
||||
Optional<String> login = SecurityUtils.getCurrentUserLogin();
|
||||
SecurityUtils.clearUserRoles();
|
||||
|
||||
assertThat(login).describedAs("precondition failed").contains(fakeUserName);
|
||||
assertThat(SecurityUtils.getCurrentUserLogin()).describedAs("precondition failed").hasValue("dummyUser");
|
||||
}
|
||||
|
||||
public static void givenUserHavingRole(final Class<?> onClass, final Long onId, final Role role) {
|
||||
SecurityUtils.addUserRole(onClass, onId, role);
|
||||
}
|
||||
}
|
||||
|
@ -2,22 +2,18 @@ package org.hostsharing.hsadminng.service.dto;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.hostsharing.hsadminng.security.SecurityUtils;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.json.JsonTest;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenLoginUserWithRole;
|
||||
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenAuthenticatedUser;
|
||||
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenUserHavingRole;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@JsonTest
|
||||
@ -31,7 +27,8 @@ public class CustomerDTOUnitTest {
|
||||
public void testSerializationAsContractualCustomerContact() throws JsonProcessingException {
|
||||
|
||||
// given
|
||||
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(CustomerDTO.class, null, Role.ANY_CUSTOMER_USER);
|
||||
CustomerDTO given = createSomeCustomerDTO();
|
||||
|
||||
// when
|
||||
@ -50,7 +47,8 @@ public class CustomerDTOUnitTest {
|
||||
public void testSerializationAsSupporter() throws JsonProcessingException {
|
||||
|
||||
// given
|
||||
givenLoginUserWithRole(Role.SUPPORTER);
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(CustomerDTO.class, null, Role.SUPPORTER);
|
||||
CustomerDTO given = createSomeCustomerDTO();
|
||||
|
||||
// when
|
||||
@ -63,7 +61,8 @@ public class CustomerDTOUnitTest {
|
||||
@Test
|
||||
public void testDeserializeAsContractualCustomerContact() throws IOException {
|
||||
// given
|
||||
givenLoginUserWithRole(Role.CONTRACTUAL_CONTACT);
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(CustomerDTO.class, null, Role.CONTRACTUAL_CONTACT);
|
||||
String json = "{\"id\":1234,\"contractualSalutation\":\"Hallo Updated\",\"billingSalutation\":\"Moin Updated\"}";
|
||||
|
||||
// when
|
||||
@ -77,6 +76,8 @@ public class CustomerDTOUnitTest {
|
||||
assertThat(actual).isEqualToComparingFieldByField(expected);
|
||||
}
|
||||
|
||||
// --- only test fixture below ---
|
||||
|
||||
private String createExpectedJSon(CustomerDTO dto) {
|
||||
String json = // the fields in alphanumeric order:
|
||||
toJSonFieldDefinitionIfPresent("id", dto.getId()) +
|
||||
|
Loading…
Reference in New Issue
Block a user