#146 [AccessRights] simple AccessFilter for UserRoleAssignments
allows r/w for ADMINs and r/o for SUPPORTERs no entity dependent access rights implemented yet
This commit is contained in:
parent
3143f27b6c
commit
60612f6c41
@ -2,6 +2,7 @@
|
|||||||
package org.hostsharing.hsadminng.domain;
|
package org.hostsharing.hsadminng.domain;
|
||||||
|
|
||||||
import org.hostsharing.hsadminng.config.Constants;
|
import org.hostsharing.hsadminng.config.Constants;
|
||||||
|
import org.hostsharing.hsadminng.service.dto.FluentBuilder;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ import javax.validation.constraints.Size;
|
|||||||
@Entity
|
@Entity
|
||||||
@Table(name = "jhi_user")
|
@Table(name = "jhi_user")
|
||||||
|
|
||||||
public class User extends AbstractAuditingEntity implements Serializable {
|
public class User extends AbstractAuditingEntity implements FluentBuilder<User>, Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@ -92,7 +93,6 @@ public class User extends AbstractAuditingEntity implements Serializable {
|
|||||||
name = "jhi_user_authority",
|
name = "jhi_user_authority",
|
||||||
joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id") },
|
joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id") },
|
||||||
inverseJoinColumns = { @JoinColumn(name = "authority_name", referencedColumnName = "name") })
|
inverseJoinColumns = { @JoinColumn(name = "authority_name", referencedColumnName = "name") })
|
||||||
|
|
||||||
@BatchSize(size = 20)
|
@BatchSize(size = 20)
|
||||||
private Set<Authority> authorities = new HashSet<>();
|
private Set<Authority> authorities = new HashSet<>();
|
||||||
|
|
||||||
@ -100,6 +100,11 @@ public class User extends AbstractAuditingEntity implements Serializable {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public User id(final long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void setId(Long id) {
|
public void setId(Long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
// Licensed under Apache-2.0
|
// Licensed under Apache-2.0
|
||||||
package org.hostsharing.hsadminng.domain;
|
package org.hostsharing.hsadminng.domain;
|
||||||
|
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
import org.hostsharing.hsadminng.repository.UserRepository;
|
||||||
|
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
||||||
|
import org.hostsharing.hsadminng.service.accessfilter.*;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.core.TreeNode;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import org.springframework.boot.jackson.JsonComponent;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
@ -16,31 +23,42 @@ import javax.validation.constraints.*;
|
|||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "user_role_assignment")
|
@Table(name = "user_role_assignment")
|
||||||
public class UserRoleAssignment implements Serializable {
|
@EntityTypeId(UserRoleAssignment.ENTITY_TYPE_ID)
|
||||||
|
public class UserRoleAssignment implements AccessMappings {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public static final String ENTITY_TYPE_ID = "rights.UserRoleAssignment";
|
||||||
|
|
||||||
|
static final String USER_FIELD_NAME = "user";
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
|
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
|
||||||
@SequenceGenerator(name = "sequenceGenerator")
|
@SequenceGenerator(name = "sequenceGenerator")
|
||||||
|
@SelfId(resolver = UserRoleAssignmentService.class)
|
||||||
|
@AccessFor(read = Role.SUPPORTER)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Size(max = 32)
|
@Size(max = 32)
|
||||||
@Column(name = "entity_type_id", length = 32, nullable = false)
|
@Column(name = "entity_type_id", length = 32, nullable = false)
|
||||||
|
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = Role.SUPPORTER)
|
||||||
private String entityTypeId;
|
private String entityTypeId;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Column(name = "entity_object_id", nullable = false)
|
@Column(name = "entity_object_id", nullable = false)
|
||||||
|
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = Role.SUPPORTER)
|
||||||
private Long entityObjectId;
|
private Long entityObjectId;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(name = "assigned_role", nullable = false)
|
@Column(name = "assigned_role", nullable = false)
|
||||||
|
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = Role.SUPPORTER)
|
||||||
private Role assignedRole;
|
private Role assignedRole;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JsonIgnoreProperties("requireds")
|
@JsonIgnoreProperties("requireds")
|
||||||
|
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = Role.SUPPORTER)
|
||||||
private User user;
|
private User user;
|
||||||
|
|
||||||
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
|
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
|
||||||
@ -49,6 +67,11 @@ public class UserRoleAssignment implements Serializable {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserRoleAssignment id(final long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void setId(Long id) {
|
public void setId(Long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
@ -136,4 +159,49 @@ public class UserRoleAssignment implements Serializable {
|
|||||||
", assignedRole='" + getAssignedRole() + "'" +
|
", assignedRole='" + getAssignedRole() + "'" +
|
||||||
"}";
|
"}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonComponent
|
||||||
|
public static class UserRoleAssignmentJsonSerializer extends JsonSerializerWithAccessFilter<UserRoleAssignment> {
|
||||||
|
|
||||||
|
public UserRoleAssignmentJsonSerializer(
|
||||||
|
final ApplicationContext ctx,
|
||||||
|
final UserRoleAssignmentService userRoleAssignmentService) {
|
||||||
|
super(ctx, userRoleAssignmentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSonFieldWriter<UserRoleAssignment> jsonFieldWriter(final Field field) {
|
||||||
|
if (USER_FIELD_NAME.equals(field.getName())) {
|
||||||
|
return (final UserRoleAssignment dto, final JsonGenerator jsonGenerator) -> {
|
||||||
|
jsonGenerator.writeNumberField(USER_FIELD_NAME, dto.getUser().getId());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return super.jsonFieldWriter(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonComponent
|
||||||
|
public static class UserRoleAssignmentJsonDeserializer extends JsonDeserializerWithAccessFilter<UserRoleAssignment> {
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
public UserRoleAssignmentJsonDeserializer(
|
||||||
|
final UserRepository userRepository,
|
||||||
|
final ApplicationContext ctx,
|
||||||
|
final UserRoleAssignmentService userRoleAssignmentService) {
|
||||||
|
super(ctx, userRoleAssignmentService);
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSonFieldReader<UserRoleAssignment> jsonFieldReader(final TreeNode treeNode, final Field field) {
|
||||||
|
if ("user".equals(field.getName())) {
|
||||||
|
return (final UserRoleAssignment target) -> {
|
||||||
|
target.setUser(userRepository.getOne(getSubNode(treeNode, "id")));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.jsonFieldReader(treeNode, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
@Transactional
|
@Transactional
|
||||||
public class UserRoleAssignmentService {
|
public class UserRoleAssignmentService implements IdToDtoResolver<UserRoleAssignment> {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(UserRoleAssignmentService.class);
|
private final Logger log = LoggerFactory.getLogger(UserRoleAssignmentService.class);
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
abstract class JSonAccessFilter<T> {
|
abstract class JSonAccessFilter<T extends AccessMappings> {
|
||||||
|
|
||||||
private final ApplicationContext ctx;
|
private final ApplicationContext ctx;
|
||||||
private final UserRoleAssignmentService userRoleAssignmentService;
|
private final UserRoleAssignmentService userRoleAssignmentService;
|
||||||
|
@ -1,211 +0,0 @@
|
|||||||
// Licensed under Apache-2.0
|
|
||||||
package org.hostsharing.hsadminng.service.accessfilter;
|
|
||||||
|
|
||||||
import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked;
|
|
||||||
|
|
||||||
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
|
||||||
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
|
||||||
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
|
||||||
import com.fasterxml.jackson.core.TreeNode;
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
|
||||||
import com.fasterxml.jackson.databind.node.*;
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Actual implementation of JSON deserialization, where {link JSonDeserializerWithAccessFilter}
|
|
||||||
* is a stateless bean, {@link JSonDeserializationWithAccessFilter} exists only during the actual
|
|
||||||
* deserialization and contains a deserialization state.
|
|
||||||
*
|
|
||||||
* @param <T> DTO class to serialize
|
|
||||||
*/
|
|
||||||
public class JSonDeserializationWithAccessFilter<T> extends JSonAccessFilter<T> {
|
|
||||||
|
|
||||||
private final TreeNode treeNode;
|
|
||||||
private final Set<Field> updatingFields = new HashSet<>();
|
|
||||||
|
|
||||||
public JSonDeserializationWithAccessFilter(
|
|
||||||
final ApplicationContext ctx,
|
|
||||||
final UserRoleAssignmentService userRoleAssignmentService,
|
|
||||||
final JsonParser jsonParser,
|
|
||||||
final DeserializationContext deserializationContext,
|
|
||||||
Class<T> dtoClass) {
|
|
||||||
super(ctx, userRoleAssignmentService, unchecked(dtoClass::newInstance));
|
|
||||||
this.treeNode = unchecked(() -> jsonParser.getCodec().readTree(jsonParser));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jackson deserializes from the JsonParser, thus no input parameter needed.
|
|
||||||
public T deserialize() {
|
|
||||||
deserializeValues();
|
|
||||||
final T currentDto = loadCurrentDto(getId());
|
|
||||||
overwriteUnmodifiedFieldsWithCurrentValues(currentDto);
|
|
||||||
checkAccessToWrittenFields(currentDto);
|
|
||||||
return dto;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deserializeValues() {
|
|
||||||
treeNode.fieldNames().forEachRemaining(fieldName -> {
|
|
||||||
try {
|
|
||||||
final Field field = dto.getClass().getDeclaredField(fieldName);
|
|
||||||
final Object newValue = readValueFromJSon(treeNode, field);
|
|
||||||
writeValueToDto(dto, field, newValue);
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
throw new RuntimeException("setting field " + fieldName + " failed", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private T loadCurrentDto(final Long id) {
|
|
||||||
if (id != null) {
|
|
||||||
return (T) loadDto(selfIdField.getAnnotation(SelfId.class).resolver(), id);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void overwriteUnmodifiedFieldsWithCurrentValues(final T currentDto) {
|
|
||||||
if (currentDto == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (Field field : currentDto.getClass().getDeclaredFields()) {
|
|
||||||
if (field.isAnnotationPresent(AccessFor.class)) {
|
|
||||||
boolean updatingField = updatingFields.contains(field);
|
|
||||||
if (updatingField && !isActuallyUpdated(field, dto, currentDto)) {
|
|
||||||
updatingFields.remove(field);
|
|
||||||
updatingField = false;
|
|
||||||
}
|
|
||||||
if (!updatingField) {
|
|
||||||
final Object value = ReflectionUtil.getValue(currentDto, field);
|
|
||||||
ReflectionUtil.setValue(dto, field, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object readValueFromJSon(final TreeNode treeNode, final Field field) {
|
|
||||||
return readValueFromJSon(treeNode, field.getName(), field.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object readValueFromJSon(final TreeNode treeNode, final String fieldName, final Class<?> fieldClass) {
|
|
||||||
final TreeNode fieldNode = treeNode.get(fieldName);
|
|
||||||
if (fieldNode instanceof NullNode) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (fieldNode instanceof TextNode) {
|
|
||||||
return ((TextNode) fieldNode).asText();
|
|
||||||
}
|
|
||||||
if (fieldNode instanceof IntNode) {
|
|
||||||
return ((IntNode) fieldNode).asInt();
|
|
||||||
}
|
|
||||||
if (fieldNode instanceof LongNode) {
|
|
||||||
return ((LongNode) fieldNode).asLong();
|
|
||||||
}
|
|
||||||
if (fieldNode instanceof DoubleNode) {
|
|
||||||
// TODO: we need to figure out, why DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS does not work
|
|
||||||
return ((DoubleNode) fieldNode).asDouble();
|
|
||||||
}
|
|
||||||
if (fieldNode instanceof ArrayNode && LocalDate.class.isAssignableFrom(fieldClass)) {
|
|
||||||
return LocalDate.of(
|
|
||||||
((ArrayNode) fieldNode).get(0).asInt(),
|
|
||||||
((ArrayNode) fieldNode).get(1).asInt(),
|
|
||||||
((ArrayNode) fieldNode).get(2).asInt());
|
|
||||||
}
|
|
||||||
throw new NotImplementedException(
|
|
||||||
"JSon node type not implemented: " + fieldNode.getClass() + " -> " + fieldName + ": " + fieldClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeValueToDto(final T dto, final Field field, final Object value) {
|
|
||||||
if (value == null) {
|
|
||||||
ReflectionUtil.setValue(dto, field, null);
|
|
||||||
} else if (field.getType().isAssignableFrom(value.getClass())) {
|
|
||||||
ReflectionUtil.setValue(dto, field, value);
|
|
||||||
} else if (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 if (BigDecimal.class.isAssignableFrom(field.getType())) {
|
|
||||||
ReflectionUtil.setValue(dto, field, new BigDecimal(value.toString()));
|
|
||||||
} else if (Boolean.class.isAssignableFrom(field.getType()) || boolean.class.isAssignableFrom(field.getType())) {
|
|
||||||
ReflectionUtil.setValue(dto, field, Boolean.valueOf(value.toString()));
|
|
||||||
} else if (field.getType().isEnum()) {
|
|
||||||
ReflectionUtil.setValue(dto, field, ReflectionUtil.asEnumValue(field.getType(), value));
|
|
||||||
} else if (LocalDate.class.isAssignableFrom(field.getType())) {
|
|
||||||
ReflectionUtil.setValue(dto, field, LocalDate.parse(value.toString()));
|
|
||||||
} else {
|
|
||||||
throw new NotImplementedException("property type not yet implemented: " + field);
|
|
||||||
}
|
|
||||||
updatingFields.add(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkAccessToWrittenFields(final T currentDto) {
|
|
||||||
updatingFields.forEach(
|
|
||||||
field -> {
|
|
||||||
// TODO this ugly code needs cleanup
|
|
||||||
if (!field.equals(selfIdField)) {
|
|
||||||
final Set<Role> roles = getLoginUserRoles();
|
|
||||||
if (isInitAccess()) {
|
|
||||||
if (!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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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 boolean isAllowedToInit(final Set<Role> roles, final Field field) {
|
|
||||||
for (Role role : roles) {
|
|
||||||
if (role.isAllowedToInit(field)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isAllowedToUpdate(final Set<Role> roles, final Field field) {
|
|
||||||
for (Role role : roles) {
|
|
||||||
if (role.isAllowedToUpdate(field)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInitAccess() {
|
|
||||||
return getId() == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isActuallyUpdated(final Field field, final T dto, T currentDto) {
|
|
||||||
return 0 != ObjectUtils.compare(ReflectionUtil.getValue(dto, field), ReflectionUtil.getValue(currentDto, field));
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,19 @@
|
|||||||
|
// Licensed under Apache-2.0
|
||||||
|
package org.hostsharing.hsadminng.service.accessfilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a JSON node value.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface JSonFieldReader<T extends AccessMappings> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a JSON node value.
|
||||||
|
*
|
||||||
|
* @param target your target entity or DTO type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void readInto(T target);
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
// Licensed under Apache-2.0
|
||||||
|
package org.hostsharing.hsadminng.service.accessfilter;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to a BiConsumer<T, JsonGenerator>, but declaring IOException as needed by JsonGenerator.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface JSonFieldWriter<T extends AccessMappings> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a JSON field and value.
|
||||||
|
*
|
||||||
|
* @param object your entity or DTO type
|
||||||
|
* @param jsonGenerator provides low level methods for writing JSON fields
|
||||||
|
*/
|
||||||
|
void write(T object, JsonGenerator jsonGenerator) throws IOException;
|
||||||
|
}
|
@ -1,92 +0,0 @@
|
|||||||
// Licensed under Apache-2.0
|
|
||||||
package org.hostsharing.hsadminng.service.accessfilter;
|
|
||||||
|
|
||||||
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
|
||||||
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
|
||||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Actual implementation of JSON serialization, where {link JsonSerializerWithAccessFilter}
|
|
||||||
* is a stateless bean, {@link JSonSerializationWithAccessFilter} exists only during the actual
|
|
||||||
* serialization and contains a serialization state.
|
|
||||||
*
|
|
||||||
* @param <T> DTO class to serialize
|
|
||||||
*/
|
|
||||||
public class JSonSerializationWithAccessFilter<T> extends JSonAccessFilter<T> {
|
|
||||||
|
|
||||||
private final JsonGenerator jsonGenerator;
|
|
||||||
private final SerializerProvider serializerProvider;
|
|
||||||
|
|
||||||
public JSonSerializationWithAccessFilter(
|
|
||||||
final ApplicationContext ctx,
|
|
||||||
final UserRoleAssignmentService userRoleAssignmentService,
|
|
||||||
final JsonGenerator jsonGenerator,
|
|
||||||
final SerializerProvider serializerProvider,
|
|
||||||
final T dto) {
|
|
||||||
super(ctx, userRoleAssignmentService, dto);
|
|
||||||
this.jsonGenerator = jsonGenerator;
|
|
||||||
this.serializerProvider = serializerProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jackson serializes into the JsonGenerator, thus no return value needed.
|
|
||||||
public void serialize() throws IOException {
|
|
||||||
|
|
||||||
jsonGenerator.writeStartObject();
|
|
||||||
for (Field field : dto.getClass().getDeclaredFields()) {
|
|
||||||
toJSon(dto, jsonGenerator, field);
|
|
||||||
}
|
|
||||||
jsonGenerator.writeEndObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toJSon(final Object dto, final JsonGenerator jsonGenerator, final Field field) throws IOException {
|
|
||||||
if (isAllowedToRead(getLoginUserRoles(), field)) {
|
|
||||||
final String fieldName = field.getName();
|
|
||||||
// TODO: maybe replace by serializerProvider.defaultSerialize...()?
|
|
||||||
// But that makes it difficult for parallel structure with the deserializer (clumsy API).
|
|
||||||
// Alternatively extract the supported types to subclasses of some abstract class and
|
|
||||||
// here as well as in the deserializer just access the matching implementation through a map.
|
|
||||||
// Or even completely switch from Jackson to GSON?
|
|
||||||
final Object fieldValue = ReflectionUtil.getValue(dto, field);
|
|
||||||
if (fieldValue == null) {
|
|
||||||
jsonGenerator.writeNullField(fieldName);
|
|
||||||
} else if (String.class.isAssignableFrom(field.getType())) {
|
|
||||||
jsonGenerator.writeStringField(fieldName, (String) fieldValue);
|
|
||||||
} else if (Integer.class.isAssignableFrom(field.getType()) || int.class.isAssignableFrom(field.getType())) {
|
|
||||||
jsonGenerator.writeNumberField(fieldName, (int) fieldValue);
|
|
||||||
} else if (Long.class.isAssignableFrom(field.getType()) || long.class.isAssignableFrom(field.getType())) {
|
|
||||||
jsonGenerator.writeNumberField(fieldName, (long) fieldValue);
|
|
||||||
} else if (LocalDate.class.isAssignableFrom(field.getType())) {
|
|
||||||
jsonGenerator.writeStringField(fieldName, fieldValue.toString());
|
|
||||||
} else if (Enum.class.isAssignableFrom(field.getType())) {
|
|
||||||
jsonGenerator.writeStringField(fieldName, ((Enum) fieldValue).name());
|
|
||||||
} else if (Boolean.class.isAssignableFrom(field.getType()) || boolean.class.isAssignableFrom(field.getType())) {
|
|
||||||
jsonGenerator.writeBooleanField(fieldName, (Boolean) fieldValue);
|
|
||||||
} else if (BigDecimal.class.isAssignableFrom(field.getType())) {
|
|
||||||
jsonGenerator.writeNumberField(fieldName, (BigDecimal) fieldValue);
|
|
||||||
} else {
|
|
||||||
throw new NotImplementedException("property type not yet implemented: " + field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isAllowedToRead(final Set<Role> roles, final Field field) {
|
|
||||||
for (Role role : roles) {
|
|
||||||
if (role.isAllowedToRead(field)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Role.ANYBODY.isAllowedToRead(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,15 +1,30 @@
|
|||||||
// Licensed under Apache-2.0
|
// Licensed under Apache-2.0
|
||||||
package org.hostsharing.hsadminng.service.accessfilter;
|
package org.hostsharing.hsadminng.service.accessfilter;
|
||||||
|
|
||||||
|
import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked;
|
||||||
|
|
||||||
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
||||||
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
||||||
|
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.core.TreeNode;
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.*;
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public abstract class JsonDeserializerWithAccessFilter<T extends AccessMappings> extends JsonDeserializer<T> {
|
public abstract class JsonDeserializerWithAccessFilter<T extends AccessMappings> extends JsonDeserializer<T> {
|
||||||
|
|
||||||
private final ApplicationContext ctx;
|
private final ApplicationContext ctx;
|
||||||
@ -29,11 +44,223 @@ public abstract class JsonDeserializerWithAccessFilter<T extends AccessMappings>
|
|||||||
|
|
||||||
final Class<T> dtoClass = ReflectionUtil
|
final Class<T> dtoClass = ReflectionUtil
|
||||||
.determineGenericClassParameter(this.getClass(), JsonDeserializerWithAccessFilter.class, 0);
|
.determineGenericClassParameter(this.getClass(), JsonDeserializerWithAccessFilter.class, 0);
|
||||||
return new JSonDeserializationWithAccessFilter<T>(
|
// @formatter:off
|
||||||
ctx,
|
return new JSonDeserializationWithAccessFilter(
|
||||||
userRoleAssignmentService,
|
this, ctx, userRoleAssignmentService, jsonParser, deserializationContext, dtoClass)
|
||||||
jsonParser,
|
.deserialize();
|
||||||
deserializationContext,
|
// @formatter:on
|
||||||
dtoClass).deserialize();
|
}
|
||||||
|
|
||||||
|
protected JSonFieldReader<T> jsonFieldReader(final TreeNode treeNode, final Field field) {
|
||||||
|
return (final T object) -> {
|
||||||
|
final Object newValue = readValueFromJSon(treeNode, field);
|
||||||
|
writeValueToDto(object, field, newValue);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Long getSubNode(final TreeNode node, final String name) {
|
||||||
|
if (!node.isObject()) {
|
||||||
|
throw new IllegalArgumentException(node + " is not a JSON object");
|
||||||
|
}
|
||||||
|
final ObjectNode objectNode = (ObjectNode) node;
|
||||||
|
final JsonNode subNode = objectNode.get(name);
|
||||||
|
if (!subNode.isNumber()) {
|
||||||
|
throw new IllegalArgumentException(node + "." + name + " is not a number");
|
||||||
|
}
|
||||||
|
return subNode.asLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readValueFromJSon(final TreeNode treeNode, final Field field) {
|
||||||
|
return readValueFromJSon(treeNode, field.getName(), field.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readValueFromJSon(final TreeNode treeNode, final String fieldName, final Class<?> fieldClass) {
|
||||||
|
// FIXME can be removed? final TreeNode fieldNode = treeNode.get(fieldName);
|
||||||
|
final TreeNode fieldNode = treeNode;
|
||||||
|
if (fieldNode instanceof NullNode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (fieldNode instanceof TextNode) {
|
||||||
|
return ((TextNode) fieldNode).asText();
|
||||||
|
}
|
||||||
|
if (fieldNode instanceof IntNode) {
|
||||||
|
return ((IntNode) fieldNode).asInt();
|
||||||
|
}
|
||||||
|
if (fieldNode instanceof LongNode) {
|
||||||
|
return ((LongNode) fieldNode).asLong();
|
||||||
|
}
|
||||||
|
if (fieldNode instanceof DoubleNode) {
|
||||||
|
// TODO: we need to figure out, why DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS does not work
|
||||||
|
return ((DoubleNode) fieldNode).asDouble();
|
||||||
|
}
|
||||||
|
if (fieldNode instanceof ArrayNode && LocalDate.class.isAssignableFrom(fieldClass)) {
|
||||||
|
return LocalDate.of(
|
||||||
|
((ArrayNode) fieldNode).get(0).asInt(),
|
||||||
|
((ArrayNode) fieldNode).get(1).asInt(),
|
||||||
|
((ArrayNode) fieldNode).get(2).asInt());
|
||||||
|
}
|
||||||
|
throw new NotImplementedException(
|
||||||
|
"JSon node type not implemented: " + fieldNode.getClass() + " -> " + fieldName + ": " + fieldClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeValueToDto(final T dto, final Field field, final Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
ReflectionUtil.setValue(dto, field, null);
|
||||||
|
} else if (field.getType().isAssignableFrom(value.getClass())) {
|
||||||
|
ReflectionUtil.setValue(dto, field, value);
|
||||||
|
} else if (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 if (BigDecimal.class.isAssignableFrom(field.getType())) {
|
||||||
|
ReflectionUtil.setValue(dto, field, new BigDecimal(value.toString()));
|
||||||
|
} else if (Boolean.class.isAssignableFrom(field.getType()) || boolean.class.isAssignableFrom(field.getType())) {
|
||||||
|
ReflectionUtil.setValue(dto, field, Boolean.valueOf(value.toString()));
|
||||||
|
} else if (field.getType().isEnum()) {
|
||||||
|
ReflectionUtil.setValue(dto, field, ReflectionUtil.asEnumValue(field.getType(), value));
|
||||||
|
} else if (LocalDate.class.isAssignableFrom(field.getType())) {
|
||||||
|
ReflectionUtil.setValue(dto, field, LocalDate.parse(value.toString()));
|
||||||
|
} else {
|
||||||
|
throw new NotImplementedException("property type not yet implemented: " + field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation of JSON deserialization, where {@link JsonDeserializerWithAccessFilter}
|
||||||
|
* is a stateless bean, this inner class exists only during the actual deserialization and contains
|
||||||
|
* the deserialization state.
|
||||||
|
*/
|
||||||
|
private class JSonDeserializationWithAccessFilter extends JSonAccessFilter<T> {
|
||||||
|
|
||||||
|
private final TreeNode treeNode;
|
||||||
|
private final Set<Field> updatingFields = new HashSet<>();
|
||||||
|
|
||||||
|
public JSonDeserializationWithAccessFilter(
|
||||||
|
final JsonDeserializerWithAccessFilter deserializer,
|
||||||
|
final ApplicationContext ctx,
|
||||||
|
final UserRoleAssignmentService userRoleAssignmentService,
|
||||||
|
final JsonParser jsonParser,
|
||||||
|
final DeserializationContext deserializationContext,
|
||||||
|
Class<T> dtoClass) {
|
||||||
|
super(ctx, userRoleAssignmentService, unchecked(dtoClass::newInstance));
|
||||||
|
this.treeNode = unchecked(() -> jsonParser.getCodec().readTree(jsonParser));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jackson deserializes from the JsonParser, thus no input parameter needed.
|
||||||
|
public T deserialize() {
|
||||||
|
deserializeValues();
|
||||||
|
final T currentDto = loadCurrentDto(getId());
|
||||||
|
overwriteUnmodifiedFieldsWithCurrentValues(currentDto);
|
||||||
|
checkAccessToWrittenFields(currentDto);
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deserializeValues() {
|
||||||
|
treeNode.fieldNames().forEachRemaining(fieldName -> {
|
||||||
|
try {
|
||||||
|
final Field field = dto.getClass().getDeclaredField(fieldName);
|
||||||
|
final TreeNode node = treeNode.get(fieldName);
|
||||||
|
jsonFieldReader(node, field).readInto(dto);
|
||||||
|
updatingFields.add(field);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
throw new RuntimeException("setting field " + fieldName + " failed", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private T loadCurrentDto(final Long id) {
|
||||||
|
if (id != null) {
|
||||||
|
return (T) loadDto(selfIdField.getAnnotation(SelfId.class).resolver(), id);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void overwriteUnmodifiedFieldsWithCurrentValues(final T currentDto) {
|
||||||
|
if (currentDto == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Field field : currentDto.getClass().getDeclaredFields()) {
|
||||||
|
if (field.isAnnotationPresent(AccessFor.class)) {
|
||||||
|
boolean updatingField = updatingFields.contains(field);
|
||||||
|
if (updatingField && !isActuallyUpdated(field, dto, currentDto)) {
|
||||||
|
updatingFields.remove(field);
|
||||||
|
updatingField = false;
|
||||||
|
}
|
||||||
|
if (!updatingField) {
|
||||||
|
final Object value = ReflectionUtil.getValue(currentDto, field);
|
||||||
|
ReflectionUtil.setValue(dto, field, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAccessToWrittenFields(final T currentDto) {
|
||||||
|
updatingFields.forEach(
|
||||||
|
field -> {
|
||||||
|
// TODO this ugly code needs cleanup
|
||||||
|
if (!field.equals(selfIdField)) {
|
||||||
|
final Set<Role> roles = getLoginUserRoles();
|
||||||
|
if (isInitAccess()) {
|
||||||
|
if (!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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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 boolean isAllowedToInit(final Set<Role> roles, final Field field) {
|
||||||
|
for (Role role : roles) {
|
||||||
|
if (role.isAllowedToInit(field)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAllowedToUpdate(final Set<Role> roles, final Field field) {
|
||||||
|
for (Role role : roles) {
|
||||||
|
if (role.isAllowedToUpdate(field)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInitAccess() {
|
||||||
|
return getId() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <F> boolean isActuallyUpdated(final Field field, final T dto, T currentDto) {
|
||||||
|
final Object o1 = ReflectionUtil.getValue(dto, field);
|
||||||
|
final Object o2 = ReflectionUtil.getValue(currentDto, field);
|
||||||
|
if (o1 != null && o2 != null && o1 instanceof Comparable && o2 instanceof Comparable) {
|
||||||
|
return 0 != ((Comparable) o1).compareTo(o2);
|
||||||
|
}
|
||||||
|
return ObjectUtils.notEqual(o1, o2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -2,14 +2,20 @@
|
|||||||
package org.hostsharing.hsadminng.service.accessfilter;
|
package org.hostsharing.hsadminng.service.accessfilter;
|
||||||
|
|
||||||
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
||||||
|
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
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.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base class for a Spring bean for JSON serialization with field-based access filters.
|
* A base class for a Spring bean for JSON serialization with field-based access filters.
|
||||||
@ -37,7 +43,96 @@ public abstract class JsonSerializerWithAccessFilter<T extends AccessMappings> e
|
|||||||
final JsonGenerator jsonGenerator,
|
final JsonGenerator jsonGenerator,
|
||||||
final SerializerProvider serializerProvider) throws IOException {
|
final SerializerProvider serializerProvider) throws IOException {
|
||||||
|
|
||||||
new JSonSerializationWithAccessFilter<T>(ctx, userRoleAssignmentService, jsonGenerator, serializerProvider, dto)
|
new JSonSerializationWithAccessFilter(this, ctx, userRoleAssignmentService, jsonGenerator, serializerProvider, dto)
|
||||||
.serialize();
|
.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected JSonFieldWriter<T> jsonFieldWriter(final Field field) {
|
||||||
|
|
||||||
|
return (final T dto, final JsonGenerator jsonGenerator) -> {
|
||||||
|
final String fieldName = field.getName();
|
||||||
|
final Object fieldValue = ReflectionUtil.getValue(dto, field);
|
||||||
|
// TODO mhoennig turn this into a dispatch table?
|
||||||
|
// TODO mhoennig: or maybe replace by serializerProvider.defaultSerialize...()?
|
||||||
|
// But the latter makes it difficult for parallel structure with the deserializer (clumsy API).
|
||||||
|
// Alternatively extract the supported types to subclasses of some abstract class and
|
||||||
|
// here as well as in the deserializer just access the matching implementation through a map.
|
||||||
|
// Or even completely switch from Jackson to GSON?
|
||||||
|
|
||||||
|
if (fieldValue == null) {
|
||||||
|
jsonGenerator.writeNullField(fieldName);
|
||||||
|
} else if (String.class.isAssignableFrom(field.getType())) {
|
||||||
|
jsonGenerator.writeStringField(fieldName, (String) fieldValue);
|
||||||
|
} else if (Integer.class.isAssignableFrom(field.getType()) || int.class.isAssignableFrom(field.getType())) {
|
||||||
|
jsonGenerator.writeNumberField(fieldName, (int) fieldValue);
|
||||||
|
} else if (Long.class.isAssignableFrom(field.getType()) || long.class.isAssignableFrom(field.getType())) {
|
||||||
|
jsonGenerator.writeNumberField(fieldName, (long) fieldValue);
|
||||||
|
} else if (LocalDate.class.isAssignableFrom(field.getType())) {
|
||||||
|
jsonGenerator.writeStringField(fieldName, fieldValue.toString());
|
||||||
|
} else if (Enum.class.isAssignableFrom(field.getType())) {
|
||||||
|
jsonGenerator.writeStringField(fieldName, ((Enum) fieldValue).name());
|
||||||
|
} else if (Boolean.class.isAssignableFrom(field.getType()) || boolean.class.isAssignableFrom(field.getType())) {
|
||||||
|
jsonGenerator.writeBooleanField(fieldName, (Boolean) fieldValue);
|
||||||
|
} else if (BigDecimal.class.isAssignableFrom(field.getType())) {
|
||||||
|
jsonGenerator.writeNumberField(fieldName, (BigDecimal) fieldValue);
|
||||||
|
} else {
|
||||||
|
throw new NotImplementedException("property type not yet implemented: " + field);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL implementation of JSON serialization, where {@link JsonSerializerWithAccessFilter}
|
||||||
|
* is a stateless bean, this inner class exists only during the actual serialization and
|
||||||
|
* contains a serialization state.
|
||||||
|
*/
|
||||||
|
private class JSonSerializationWithAccessFilter extends JSonAccessFilter<T> {
|
||||||
|
|
||||||
|
private final JsonSerializerWithAccessFilter serializer;
|
||||||
|
private final JsonGenerator jsonGenerator;
|
||||||
|
private final SerializerProvider serializerProvider;
|
||||||
|
|
||||||
|
public JSonSerializationWithAccessFilter(
|
||||||
|
final JsonSerializerWithAccessFilter serializer,
|
||||||
|
final ApplicationContext ctx,
|
||||||
|
final UserRoleAssignmentService userRoleAssignmentService,
|
||||||
|
final JsonGenerator jsonGenerator,
|
||||||
|
final SerializerProvider serializerProvider,
|
||||||
|
final T dto) {
|
||||||
|
super(ctx, userRoleAssignmentService, dto);
|
||||||
|
this.serializer = serializer;
|
||||||
|
this.jsonGenerator = jsonGenerator;
|
||||||
|
this.serializerProvider = serializerProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jackson serializes into the JsonGenerator, thus no return value needed.
|
||||||
|
public void serialize() throws IOException {
|
||||||
|
|
||||||
|
jsonGenerator.writeStartObject();
|
||||||
|
for (Field field : dto.getClass().getDeclaredFields()) {
|
||||||
|
toJSon(dto, jsonGenerator, field);
|
||||||
|
}
|
||||||
|
jsonGenerator.writeEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeJSonField(final T dto, final Field field, final JsonGenerator jsonGenerator) throws IOException {
|
||||||
|
serializer.jsonFieldWriter(field).write(dto, jsonGenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toJSon(final T dto, final JsonGenerator jsonGenerator, final Field field) throws IOException {
|
||||||
|
if (isAllowedToRead(getLoginUserRoles(), field)) {
|
||||||
|
writeJSonField(dto, field, jsonGenerator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAllowedToRead(final Set<Role> roles, final Field field) {
|
||||||
|
for (Role role : roles) {
|
||||||
|
if (role.isAllowedToRead(field)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Role.ANYBODY.isAllowedToRead(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -48,15 +48,15 @@ public class MembershipDTO implements AccessMappings, FluentBuilder<MembershipDT
|
|||||||
@AccessFor(init = Role.ADMIN, read = { Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT })
|
@AccessFor(init = Role.ADMIN, read = { Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT })
|
||||||
private Long customerId;
|
private Long customerId;
|
||||||
|
|
||||||
@AccessFor(init = Role.ADMIN, read = { Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT })
|
@AccessFor(update = Role.IGNORED, read = { Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT })
|
||||||
private String customerPrefix;
|
private String customerPrefix;
|
||||||
|
|
||||||
@AccessFor(init = Role.ANYBODY, update = Role.ANYBODY, read = Role.FINANCIAL_CONTACT)
|
@AccessFor(update = Role.IGNORED, read = Role.FINANCIAL_CONTACT)
|
||||||
private String displayLabel;
|
|
||||||
|
|
||||||
@AccessFor(init = Role.ANYBODY, update = Role.ANYBODY, read = Role.FINANCIAL_CONTACT)
|
|
||||||
private String customerDisplayLabel;
|
private String customerDisplayLabel;
|
||||||
|
|
||||||
|
@AccessFor(update = Role.IGNORED, read = Role.FINANCIAL_CONTACT)
|
||||||
|
private String displayLabel;
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -121,14 +121,6 @@ public class MembershipDTO implements AccessMappings, FluentBuilder<MembershipDT
|
|||||||
this.customerPrefix = customerPrefix;
|
this.customerPrefix = customerPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDisplayLabel() {
|
|
||||||
return displayLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDisplayLabel(final String displayLabel) {
|
|
||||||
this.displayLabel = displayLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCustomerDisplayLabel() {
|
public String getCustomerDisplayLabel() {
|
||||||
return customerDisplayLabel;
|
return customerDisplayLabel;
|
||||||
}
|
}
|
||||||
@ -137,6 +129,14 @@ public class MembershipDTO implements AccessMappings, FluentBuilder<MembershipDT
|
|||||||
this.customerDisplayLabel = customerDisplayLabel;
|
this.customerDisplayLabel = customerDisplayLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDisplayLabel() {
|
||||||
|
return displayLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisplayLabel(final String displayLabel) {
|
||||||
|
this.displayLabel = displayLabel;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
@ -168,14 +168,16 @@ public class MembershipDTO implements AccessMappings, FluentBuilder<MembershipDT
|
|||||||
", memberUntilDate='" + getMemberUntilDate() + "'" +
|
", memberUntilDate='" + getMemberUntilDate() + "'" +
|
||||||
", remark='" + getRemark() + "'" +
|
", remark='" + getRemark() + "'" +
|
||||||
", customer=" + getCustomerId() +
|
", customer=" + getCustomerId() +
|
||||||
", customer='" + getCustomerPrefix() + "'" +
|
", customerPrefix='" + getCustomerPrefix() + "'" +
|
||||||
|
", customerDisplayLabel='" + getCustomerDisplayLabel() + "'" +
|
||||||
|
", displayLabel='" + getDisplayLabel() + "'" +
|
||||||
"}";
|
"}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonComponent
|
@JsonComponent
|
||||||
public static class MembershipJsonSerializer extends JsonSerializerWithAccessFilter<MembershipDTO> {
|
public static class JsonSerializer extends JsonSerializerWithAccessFilter<MembershipDTO> {
|
||||||
|
|
||||||
public MembershipJsonSerializer(
|
public JsonSerializer(
|
||||||
final ApplicationContext ctx,
|
final ApplicationContext ctx,
|
||||||
final UserRoleAssignmentService userRoleAssignmentService) {
|
final UserRoleAssignmentService userRoleAssignmentService) {
|
||||||
super(ctx, userRoleAssignmentService);
|
super(ctx, userRoleAssignmentService);
|
||||||
@ -183,9 +185,9 @@ public class MembershipDTO implements AccessMappings, FluentBuilder<MembershipDT
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JsonComponent
|
@JsonComponent
|
||||||
public static class MembershipJsonDeserializer extends JsonDeserializerWithAccessFilter<MembershipDTO> {
|
public static class JsonDeserializer extends JsonDeserializerWithAccessFilter<MembershipDTO> {
|
||||||
|
|
||||||
public MembershipJsonDeserializer(
|
public JsonDeserializer(
|
||||||
final ApplicationContext ctx,
|
final ApplicationContext ctx,
|
||||||
final UserRoleAssignmentService userRoleAssignmentService) {
|
final UserRoleAssignmentService userRoleAssignmentService) {
|
||||||
super(ctx, userRoleAssignmentService);
|
super(ctx, userRoleAssignmentService);
|
||||||
|
@ -49,7 +49,7 @@ public class JSonAccessFilterTestFixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EntityTypeId("test.Given")
|
@EntityTypeId("test.Given")
|
||||||
static class GivenDto implements FluentBuilder<GivenDto> {
|
static class GivenDto implements AccessMappings, FluentBuilder<GivenDto> {
|
||||||
|
|
||||||
@SelfId(resolver = GivenService.class)
|
@SelfId(resolver = GivenService.class)
|
||||||
@AccessFor(read = ANYBODY)
|
@AccessFor(read = ANYBODY)
|
||||||
@ -119,7 +119,7 @@ public class JSonAccessFilterTestFixture {
|
|||||||
static abstract class GivenChildService implements IdToDtoResolver<GivenChildDto> {
|
static abstract class GivenChildService implements IdToDtoResolver<GivenChildDto> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GivenChildDto implements FluentBuilder<GivenChildDto> {
|
public static class GivenChildDto implements AccessMappings, FluentBuilder<GivenChildDto> {
|
||||||
|
|
||||||
@SelfId(resolver = GivenChildService.class)
|
@SelfId(resolver = GivenChildService.class)
|
||||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||||
@ -133,7 +133,7 @@ public class JSonAccessFilterTestFixture {
|
|||||||
String restrictedField;
|
String restrictedField;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GivenDtoWithMultipleSelfId {
|
public static class GivenDtoWithMultipleSelfId implements AccessMappings {
|
||||||
|
|
||||||
@SelfId(resolver = GivenChildService.class)
|
@SelfId(resolver = GivenChildService.class)
|
||||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||||
@ -145,7 +145,7 @@ public class JSonAccessFilterTestFixture {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GivenDtoWithUnknownFieldType {
|
public static class GivenDtoWithUnknownFieldType implements AccessMappings {
|
||||||
|
|
||||||
@SelfId(resolver = GivenChildService.class)
|
@SelfId(resolver = GivenChildService.class)
|
||||||
@AccessFor(read = Role.ANYBODY)
|
@AccessFor(read = Role.ANYBODY)
|
||||||
|
@ -20,6 +20,9 @@ public class JSonBuilder {
|
|||||||
json.append(prop.right);
|
json.append(prop.right);
|
||||||
} else if (prop.right instanceof List) {
|
} else if (prop.right instanceof List) {
|
||||||
json.append(toJSonArray(prop.right));
|
json.append(toJSonArray(prop.right));
|
||||||
|
} else if (prop.right instanceof String && ((String) prop.right).startsWith("{\n")) {
|
||||||
|
// TODO mhoennig: find better solution for adding object nodes
|
||||||
|
json.append(prop.right);
|
||||||
} else {
|
} else {
|
||||||
json.append(inQuotes(prop.right));
|
json.append(inQuotes(prop.right));
|
||||||
}
|
}
|
||||||
@ -44,12 +47,23 @@ public class JSonBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public JSonBuilder withFieldValueIfPresent(String name, String value) {
|
public JSonBuilder withFieldValueIfPresent(String name, String value) {
|
||||||
json.append(value != null ? inQuotes(name) + ":" + inQuotes(value) + "," : "");
|
if (value != null) {
|
||||||
|
json.append(inQuotes(name) + ":" + inQuotes(value) + ",");
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSonBuilder withFieldValueIfPresent(String name, Number value) {
|
public JSonBuilder withFieldValueIfPresent(String name, Number value) {
|
||||||
json.append(value != null ? inQuotes(name) + ":" + value + "," : "");
|
if (value != null) {
|
||||||
|
json.append(inQuotes(name) + ":" + value + ",");
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E extends Enum<E>> JSonBuilder withFieldValueIfPresent(final String name, final E value) {
|
||||||
|
if (value != null) {
|
||||||
|
json.append(inQuotes(name) + ":" + inQuotes(value.name()) + ",");
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,5 +88,4 @@ public class JSonBuilder {
|
|||||||
private static String inQuotes(Object value) {
|
private static String inQuotes(Object value) {
|
||||||
return value != null ? "\"" + value.toString() + "\"" : "null";
|
return value != null ? "\"" + value.toString() + "\"" : "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import com.fasterxml.jackson.core.JsonParser;
|
|||||||
import com.fasterxml.jackson.core.ObjectCodec;
|
import com.fasterxml.jackson.core.ObjectCodec;
|
||||||
import com.fasterxml.jackson.core.TreeNode;
|
import com.fasterxml.jackson.core.TreeNode;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
@ -118,12 +119,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("openStringField", null)));
|
ImmutablePair.of("openStringField", null)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(
|
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.openStringField).isNull();
|
assertThat(actualDto.openStringField).isNull();
|
||||||
@ -139,12 +135,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("openStringField", "String Value")));
|
ImmutablePair.of("openStringField", "String Value")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(
|
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.openStringField).isEqualTo("String Value");
|
assertThat(actualDto.openStringField).isEqualTo("String Value");
|
||||||
@ -160,12 +151,9 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("openIntegerField", 1234)));
|
ImmutablePair.of("openIntegerField", 1234)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(
|
// @formatter:off
|
||||||
ctx,
|
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);;
|
||||||
userRoleAssignmentService,
|
// @formatter:on
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.openIntegerField).isEqualTo(1234);
|
assertThat(actualDto.openIntegerField).isEqualTo(1234);
|
||||||
@ -182,12 +170,8 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedBigDecimalField", SOME_BIG_DECIMAL_WITH_ANOTHER_SCALE)));
|
ImmutablePair.of("restrictedBigDecimalField", SOME_BIG_DECIMAL_WITH_ANOTHER_SCALE)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(
|
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
||||||
ctx,
|
;
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.restrictedBigDecimalField).isEqualByComparingTo(SOME_BIG_DECIMAL);
|
assertThat(actualDto.restrictedBigDecimalField).isEqualByComparingTo(SOME_BIG_DECIMAL);
|
||||||
@ -217,12 +201,8 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("openEnumField", TestEnum.GREEN)));
|
ImmutablePair.of("openEnumField", TestEnum.GREEN)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(
|
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
||||||
ctx,
|
;
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.openIntegerField).isEqualTo(11);
|
assertThat(actualDto.openIntegerField).isEqualTo(11);
|
||||||
@ -247,13 +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(
|
Throwable exception = catchThrowable(() -> deserializerForGivenDto().deserialize(jsonParser, null));
|
||||||
() -> new JSonDeserializationWithAccessFilter<>(
|
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize());
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOf(NotImplementedException.class);
|
assertThat(exception).isInstanceOf(NotImplementedException.class);
|
||||||
@ -271,12 +245,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedField", "update value of restricted field")));
|
ImmutablePair.of("restrictedField", "update value of restricted field")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(
|
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.restrictedField).isEqualTo("update value of restricted field");
|
assertThat(actualDto.restrictedField).isEqualTo("update value of restricted field");
|
||||||
@ -294,12 +263,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedField", "initial value of restricted field")));
|
ImmutablePair.of("restrictedField", "initial value of restricted field")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
GivenDto actualDto = new JSonDeserializationWithAccessFilter<>(
|
final GivenDto actualDto = deserializerForGivenDto().deserialize(jsonParser, null);
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.restrictedField).isEqualTo("initial value of restricted field");
|
assertThat(actualDto.restrictedField).isEqualTo("initial value of restricted field");
|
||||||
@ -316,13 +280,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedField", "updated value of restricted field")));
|
ImmutablePair.of("restrictedField", "updated value of restricted field")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Throwable exception = catchThrowable(
|
final Throwable exception = catchThrowable(() -> deserializerForGivenDto().deserialize(jsonParser, null));
|
||||||
() -> new JSonDeserializationWithAccessFilter<>(
|
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize());
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||||
@ -342,13 +300,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedField", "another value of restricted field")));
|
ImmutablePair.of("restrictedField", "another value of restricted field")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Throwable exception = catchThrowable(
|
final Throwable exception = catchThrowable(() -> deserializerForGivenDto().deserialize(jsonParser, null));
|
||||||
() -> new JSonDeserializationWithAccessFilter<>(
|
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize());
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||||
@ -368,12 +320,7 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
Throwable exception = catchThrowable(
|
Throwable exception = catchThrowable(
|
||||||
() -> new JSonDeserializationWithAccessFilter<>(
|
() -> deserializerForGivenChildDto().deserialize(jsonParser, null));
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenChildDto.class).deserialize());
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||||
@ -392,13 +339,8 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("parentId", 1234L)));
|
ImmutablePair.of("parentId", 1234L)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final GivenChildDto actualDto = new JSonDeserializationWithAccessFilter<>(
|
final GivenChildDto actualDto = deserializerForGivenChildDto().deserialize(jsonParser, null);
|
||||||
ctx,
|
;
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenChildDto.class)
|
|
||||||
.deserialize();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actualDto.parentId).isEqualTo(1234L);
|
assertThat(actualDto.parentId).isEqualTo(1234L);
|
||||||
@ -416,13 +358,8 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
ImmutablePair.of("restrictedField", "Restricted String Value")));
|
ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Throwable exception = catchThrowable(
|
final Throwable exception = catchThrowable(
|
||||||
() -> new JSonDeserializationWithAccessFilter<>(
|
() -> deserializerForGivenDto().deserialize(jsonParser, null));
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDto.class).deserialize());
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||||
@ -437,14 +374,8 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
givenJSonTree(asJSon(ImmutablePair.of("id", 1111L)));
|
givenJSonTree(asJSon(ImmutablePair.of("id", 1111L)));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Throwable exception = catchThrowable(
|
final Throwable exception = catchThrowable(
|
||||||
() -> new JSonDeserializationWithAccessFilter<>(
|
() -> deserializerForGivenDtoWithMultipleSelfId().deserialize(jsonParser, null));
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDtoWithMultipleSelfId.class)
|
|
||||||
.deserialize());
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOf(AssertionError.class)
|
assertThat(exception).isInstanceOf(AssertionError.class)
|
||||||
@ -458,14 +389,8 @@ public class JSonDeserializationWithAccessFilterUnitTest {
|
|||||||
givenJSonTree(asJSon(ImmutablePair.of("unknown", new Arbitrary())));
|
givenJSonTree(asJSon(ImmutablePair.of("unknown", new Arbitrary())));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Throwable exception = catchThrowable(
|
final Throwable exception = catchThrowable(
|
||||||
() -> new JSonDeserializationWithAccessFilter<>(
|
() -> deserializerForGivenDtoWithUnknownFieldType().deserialize(jsonParser, null));
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
GivenDtoWithUnknownFieldType.class)
|
|
||||||
.deserialize());
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(exception).isInstanceOf(NotImplementedException.class)
|
assertThat(exception).isInstanceOf(NotImplementedException.class)
|
||||||
@ -482,4 +407,30 @@ 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.
|
||||||
|
// And it's down here to keep the ugly formatting out of the test cases.
|
||||||
|
|
||||||
|
public JsonDeserializerWithAccessFilter<GivenDto> deserializerForGivenDto() throws IOException {
|
||||||
|
return new JsonDeserializerWithAccessFilter<GivenDto>(ctx, userRoleAssignmentService) {
|
||||||
|
// no need to overload any method here
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonDeserializerWithAccessFilter<GivenChildDto> deserializerForGivenChildDto() throws IOException {
|
||||||
|
return new JsonDeserializerWithAccessFilter<GivenChildDto>(ctx, userRoleAssignmentService) {
|
||||||
|
// no need to overload any method here
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonDeserializer<GivenDtoWithMultipleSelfId> deserializerForGivenDtoWithMultipleSelfId() {
|
||||||
|
return new JsonDeserializerWithAccessFilter<GivenDtoWithMultipleSelfId>(ctx, userRoleAssignmentService) {
|
||||||
|
// no need to overload any method here
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonDeserializer<GivenDtoWithUnknownFieldType> deserializerForGivenDtoWithUnknownFieldType() {
|
||||||
|
return new JsonDeserializerWithAccessFilter<GivenDtoWithUnknownFieldType>(ctx, userRoleAssignmentService) {
|
||||||
|
// no need to overload any method here
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldSerializeStringField() throws IOException {
|
public void shouldSerializeStringField() throws IOException {
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
serialize(givenDTO);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeStringField("openStringField", givenDTO.openStringField);
|
verify(jsonGenerator).writeStringField("openStringField", givenDTO.openStringField);
|
||||||
@ -75,7 +75,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldSerializeIntegerField() throws IOException {
|
public void shouldSerializeIntegerField() throws IOException {
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
serialize(givenDTO);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeNumberField("openIntegerField", givenDTO.openIntegerField);
|
verify(jsonGenerator).writeNumberField("openIntegerField", givenDTO.openIntegerField);
|
||||||
@ -84,7 +84,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldSerializePrimitiveIntField() throws IOException {
|
public void shouldSerializePrimitiveIntField() throws IOException {
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
serialize(givenDTO);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeNumberField("openPrimitiveIntField", givenDTO.openPrimitiveIntField);
|
verify(jsonGenerator).writeNumberField("openPrimitiveIntField", givenDTO.openPrimitiveIntField);
|
||||||
@ -93,7 +93,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldSerializeLongField() throws IOException {
|
public void shouldSerializeLongField() throws IOException {
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
final Throwable actual = catchThrowable(() -> serialize(givenDTO));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeNumberField("openLongField", givenDTO.openLongField);
|
verify(jsonGenerator).writeNumberField("openLongField", givenDTO.openLongField);
|
||||||
@ -102,7 +102,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldSerializePrimitiveLongField() throws IOException {
|
public void shouldSerializePrimitiveLongField() throws IOException {
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
serialize(givenDTO);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeNumberField("openPrimitiveLongField", givenDTO.openPrimitiveLongField);
|
verify(jsonGenerator).writeNumberField("openPrimitiveLongField", givenDTO.openPrimitiveLongField);
|
||||||
@ -111,7 +111,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldSerializeBooleanField() throws IOException {
|
public void shouldSerializeBooleanField() throws IOException {
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
serialize(givenDTO);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeBooleanField("openBooleanField", givenDTO.openBooleanField);
|
verify(jsonGenerator).writeBooleanField("openBooleanField", givenDTO.openBooleanField);
|
||||||
@ -120,7 +120,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldSerializePrimitiveBooleanField() throws IOException {
|
public void shouldSerializePrimitiveBooleanField() throws IOException {
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
serialize(givenDTO);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeBooleanField("openPrimitiveBooleanField", givenDTO.openPrimitiveBooleanField);
|
verify(jsonGenerator).writeBooleanField("openPrimitiveBooleanField", givenDTO.openPrimitiveBooleanField);
|
||||||
@ -129,7 +129,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldSerializeBigDecimalField() throws IOException {
|
public void shouldSerializeBigDecimalField() throws IOException {
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
final Throwable actual = catchThrowable(() -> serialize(givenDTO));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeNumberField("openBigDecimalField", givenDTO.openBigDecimalField);
|
verify(jsonGenerator).writeNumberField("openBigDecimalField", givenDTO.openBigDecimalField);
|
||||||
@ -138,7 +138,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldSerializeLocalDateField() throws IOException {
|
public void shouldSerializeLocalDateField() throws IOException {
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
serialize(givenDTO);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeStringField("openLocalDateField", givenDTO.openLocalDateFieldAsString);
|
verify(jsonGenerator).writeStringField("openLocalDateField", givenDTO.openLocalDateFieldAsString);
|
||||||
@ -147,7 +147,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldSerializeEnumField() throws IOException {
|
public void shouldSerializeEnumField() throws IOException {
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
serialize(givenDTO);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeStringField("openEnumField", givenDTO.openEnumFieldAsString);
|
verify(jsonGenerator).writeStringField("openEnumField", givenDTO.openEnumFieldAsString);
|
||||||
@ -160,7 +160,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
securityContext.havingAuthenticatedUser().withRole(GivenCustomerDto.class, 888L, Role.FINANCIAL_CONTACT);
|
securityContext.havingAuthenticatedUser().withRole(GivenCustomerDto.class, 888L, Role.FINANCIAL_CONTACT);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
serialize(givenDTO);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator).writeStringField("restrictedField", givenDTO.restrictedField);
|
verify(jsonGenerator).writeStringField("restrictedField", givenDTO.restrictedField);
|
||||||
@ -173,7 +173,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
securityContext.havingAuthenticatedUser().withRole(GivenCustomerDto.class, 888L, Role.ANY_CUSTOMER_USER);
|
securityContext.havingAuthenticatedUser().withRole(GivenCustomerDto.class, 888L, Role.ANY_CUSTOMER_USER);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
new JSonSerializationWithAccessFilter<>(ctx, userRoleAssignmentService, jsonGenerator, null, givenDTO).serialize();
|
serialize(givenDTO);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(jsonGenerator, never()).writeStringField("restrictedField", givenDTO.restrictedField);
|
verify(jsonGenerator, never()).writeStringField("restrictedField", givenDTO.restrictedField);
|
||||||
@ -184,8 +184,9 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
|
|
||||||
// given
|
// given
|
||||||
class Arbitrary {
|
class Arbitrary {
|
||||||
|
|
||||||
}
|
}
|
||||||
class GivenDtoWithUnimplementedFieldType {
|
class GivenDtoWithUnimplementedFieldType implements AccessMappings {
|
||||||
|
|
||||||
@AccessFor(read = Role.ANYBODY)
|
@AccessFor(read = Role.ANYBODY)
|
||||||
Arbitrary fieldWithUnimplementedType = new Arbitrary();
|
Arbitrary fieldWithUnimplementedType = new Arbitrary();
|
||||||
@ -194,14 +195,7 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
SecurityContextFake.havingAuthenticatedUser();
|
SecurityContextFake.havingAuthenticatedUser();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final Throwable actual = catchThrowable(
|
final Throwable actual = catchThrowable(() -> serialize(givenDtoWithUnimplementedFieldType));
|
||||||
() -> new JSonSerializationWithAccessFilter<>(
|
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonGenerator,
|
|
||||||
null,
|
|
||||||
givenDtoWithUnimplementedFieldType)
|
|
||||||
.serialize());
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(actual).isInstanceOf(NotImplementedException.class);
|
assertThat(actual).isInstanceOf(NotImplementedException.class);
|
||||||
@ -209,4 +203,10 @@ public class JSonSerializationWithAccessFilterUnitTest {
|
|||||||
|
|
||||||
// --- fixture code below ---
|
// --- fixture code below ---
|
||||||
|
|
||||||
|
public <T extends AccessMappings> void serialize(final T dto) throws IOException {
|
||||||
|
// @formatter:off
|
||||||
|
new JsonSerializerWithAccessFilter<T>(ctx, userRoleAssignmentService) {}
|
||||||
|
.serialize(dto, jsonGenerator, null);
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,194 @@
|
|||||||
|
// Licensed under Apache-2.0
|
||||||
|
package org.hostsharing.hsadminng.service.dto;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||||
|
import static org.hostsharing.hsadminng.service.dto.MembershipDTOUnitTest.createSampleDTO;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
|
||||||
|
import org.hostsharing.hsadminng.domain.Customer;
|
||||||
|
import org.hostsharing.hsadminng.domain.Membership;
|
||||||
|
import org.hostsharing.hsadminng.repository.CustomerRepository;
|
||||||
|
import org.hostsharing.hsadminng.repository.MembershipRepository;
|
||||||
|
import org.hostsharing.hsadminng.security.AuthoritiesConstants;
|
||||||
|
import org.hostsharing.hsadminng.service.MembershipService;
|
||||||
|
import org.hostsharing.hsadminng.service.MembershipValidator;
|
||||||
|
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
||||||
|
import org.hostsharing.hsadminng.service.accessfilter.JSonBuilder;
|
||||||
|
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||||
|
import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock;
|
||||||
|
import org.hostsharing.hsadminng.service.mapper.CustomerMapperImpl;
|
||||||
|
import org.hostsharing.hsadminng.service.mapper.MembershipMapper;
|
||||||
|
import org.hostsharing.hsadminng.service.mapper.MembershipMapperImpl;
|
||||||
|
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.RandomUtils;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.json.JsonTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
@JsonTest
|
||||||
|
@SpringBootTest(
|
||||||
|
classes = {
|
||||||
|
CustomerMapperImpl.class,
|
||||||
|
MembershipMapperImpl.class,
|
||||||
|
MembershipMapperImpl.class,
|
||||||
|
MembershipDTO.JsonSerializer.class,
|
||||||
|
MembershipDTO.JsonDeserializer.class
|
||||||
|
})
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
public class MembershipDTOIntTest {
|
||||||
|
|
||||||
|
private static final Long SOME_CUSTOMER_ID = RandomUtils.nextLong(100, 199);
|
||||||
|
private static final Integer SOME_CUSTOMER_REFERENCE = 10001;
|
||||||
|
private static final String SOME_CUSTOMER_PREFIX = "abc";
|
||||||
|
private static final String SOME_CUSTOMER_NAME = "Some Customer Name";
|
||||||
|
private static final String SOME_CUSTOMER_DISPLAY_LABEL = "Some Customer Name [10001:abc]";
|
||||||
|
private static final Customer SOME_CUSTOMER = new Customer().id(SOME_CUSTOMER_ID)
|
||||||
|
.reference(SOME_CUSTOMER_REFERENCE)
|
||||||
|
.prefix(SOME_CUSTOMER_PREFIX)
|
||||||
|
.name(SOME_CUSTOMER_NAME);
|
||||||
|
|
||||||
|
private static final Long SOME_SEPA_MANDATE_ID = RandomUtils.nextLong(300, 399);
|
||||||
|
private static final Membership SOME_SEPA_MANDATE = new Membership().id(SOME_SEPA_MANDATE_ID).customer(SOME_CUSTOMER);
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public MockitoRule mockito = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MembershipMapper membershipMapper;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private CustomerRepository customerRepository;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private MembershipRepository membershipRepository;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private MembershipValidator membershipValidator;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private MembershipService MembershipService;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private EntityManager em;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
public UserRoleAssignmentService userRoleAssignmentService;
|
||||||
|
|
||||||
|
private SecurityContextMock securityContext;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
given(customerRepository.findById(SOME_CUSTOMER_ID)).willReturn(Optional.of(SOME_CUSTOMER));
|
||||||
|
given(membershipRepository.findById(SOME_SEPA_MANDATE_ID)).willReturn((Optional.of(SOME_SEPA_MANDATE)));
|
||||||
|
|
||||||
|
securityContext = SecurityContextMock.usingMock(userRoleAssignmentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldSerializePartiallyForFinancialCustomerContact() throws JsonProcessingException {
|
||||||
|
|
||||||
|
// given
|
||||||
|
securityContext.havingAuthenticatedUser().withRole(CustomerDTO.class, SOME_CUSTOMER_ID, Role.FINANCIAL_CONTACT);
|
||||||
|
final MembershipDTO given = createSampleDTO(SOME_SEPA_MANDATE_ID, SOME_CUSTOMER_ID);
|
||||||
|
|
||||||
|
// when
|
||||||
|
final String actual = objectMapper.writeValueAsString(given);
|
||||||
|
|
||||||
|
// then
|
||||||
|
given.setRemark(null);
|
||||||
|
assertEquals(createExpectedJSon(given), actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldSerializeCompletelyForSupporter() throws JsonProcessingException {
|
||||||
|
|
||||||
|
// given
|
||||||
|
securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.SUPPORTER);
|
||||||
|
final MembershipDTO given = createSampleDTO(SOME_SEPA_MANDATE_ID, SOME_CUSTOMER_ID);
|
||||||
|
|
||||||
|
// when
|
||||||
|
final String actual = objectMapper.writeValueAsString(given);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(createExpectedJSon(given), actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldNotDeserializeForContractualCustomerContact() {
|
||||||
|
// given
|
||||||
|
securityContext.havingAuthenticatedUser().withRole(CustomerDTO.class, SOME_CUSTOMER_ID, Role.CONTRACTUAL_CONTACT);
|
||||||
|
final String json = new JSonBuilder()
|
||||||
|
.withFieldValue("id", SOME_SEPA_MANDATE_ID)
|
||||||
|
.withFieldValue("remark", "Updated Remark")
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final Throwable actual = catchThrowable(() -> objectMapper.readValue(json, MembershipDTO.class));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(actual).isInstanceOfSatisfying(
|
||||||
|
BadRequestAlertException.class,
|
||||||
|
bre -> assertThat(bre.getMessage()).isEqualTo(
|
||||||
|
"Update of field MembershipDTO.remark prohibited for current user role(s): CONTRACTUAL_CONTACT"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldDeserializeForAdminIfRemarkIsChanged() throws IOException {
|
||||||
|
// given
|
||||||
|
securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN);
|
||||||
|
final String json = new JSonBuilder()
|
||||||
|
.withFieldValue("id", SOME_SEPA_MANDATE_ID)
|
||||||
|
.withFieldValue("remark", "Updated Remark")
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
// when
|
||||||
|
final MembershipDTO actual = objectMapper.readValue(json, MembershipDTO.class);
|
||||||
|
|
||||||
|
// then
|
||||||
|
final MembershipDTO expected = new MembershipDTO();
|
||||||
|
expected.setId(SOME_SEPA_MANDATE_ID);
|
||||||
|
expected.setCustomerId(SOME_CUSTOMER_ID);
|
||||||
|
expected.setRemark("Updated Remark");
|
||||||
|
assertThat(actual).isEqualToIgnoringGivenFields(expected, "customerPrefix", "customerDisplayLabel", "displayLabel");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- only test fixture below ---
|
||||||
|
|
||||||
|
private String createExpectedJSon(MembershipDTO dto) {
|
||||||
|
return new JSonBuilder()
|
||||||
|
.withFieldValueIfPresent("id", dto.getId())
|
||||||
|
.withFieldValueIfPresent("admissionDocumentDate", Objects.toString(dto.getAdmissionDocumentDate()))
|
||||||
|
.withFieldValueIfPresent("cancellationDocumentDate", Objects.toString(dto.getCancellationDocumentDate()))
|
||||||
|
.withFieldValueIfPresent("memberFromDate", Objects.toString(dto.getMemberFromDate()))
|
||||||
|
.withFieldValueIfPresent("memberUntilDate", Objects.toString(dto.getMemberUntilDate()))
|
||||||
|
.withFieldValueIfPresent("remark", dto.getRemark())
|
||||||
|
.withFieldValueIfPresent("customerId", dto.getCustomerId())
|
||||||
|
.withFieldValue("customerPrefix", dto.getCustomerPrefix())
|
||||||
|
.withFieldValue("customerDisplayLabel", dto.getCustomerDisplayLabel())
|
||||||
|
.withFieldValue("displayLabel", dto.getDisplayLabel())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
// Licensed under Apache-2.0
|
|
||||||
package org.hostsharing.hsadminng.service.dto;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class MembershipDTOTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void withShouldApplyCallback() {
|
|
||||||
final MembershipDTO actual = new MembershipDTO().with(m -> m.setRemark("Some Remark"));
|
|
||||||
|
|
||||||
assertThat(actual.getRemark()).isEqualTo("Some Remark");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,127 +1,104 @@
|
|||||||
// Licensed under Apache-2.0
|
// Licensed under Apache-2.0
|
||||||
package org.hostsharing.hsadminng.service.dto;
|
package org.hostsharing.hsadminng.service.dto;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.catchThrowable;
|
|
||||||
import static org.hostsharing.hsadminng.service.accessfilter.JSonBuilder.asJSon;
|
|
||||||
import static org.mockito.BDDMockito.given;
|
|
||||||
|
|
||||||
import org.hostsharing.hsadminng.security.AuthoritiesConstants;
|
|
||||||
import org.hostsharing.hsadminng.service.CustomerService;
|
|
||||||
import org.hostsharing.hsadminng.service.MembershipService;
|
|
||||||
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.JSonDeserializationWithAccessFilter;
|
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock;
|
|
||||||
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import com.fasterxml.jackson.core.ObjectCodec;
|
import org.apache.commons.lang3.RandomUtils;
|
||||||
import com.fasterxml.jackson.core.TreeNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.MockitoJUnit;
|
|
||||||
import org.mockito.junit.MockitoRule;
|
|
||||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.time.LocalDate;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class MembershipDTOUnitTest {
|
public class MembershipDTOUnitTest extends AccessMappingsUnitTestBase<MembershipDTO> {
|
||||||
|
|
||||||
@Rule
|
public MembershipDTOUnitTest() {
|
||||||
public MockitoRule mockitoRule = MockitoJUnit.rule();
|
super(MembershipDTO.class, MembershipDTOUnitTest::createSampleDTO, MembershipDTOUnitTest::createRandomDTO);
|
||||||
|
|
||||||
@Mock
|
|
||||||
private ApplicationContext ctx;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private AutowireCapableBeanFactory autowireCapableBeanFactory;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private JsonParser jsonParser;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private ObjectCodec codec;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private TreeNode treeNode;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private UserRoleAssignmentService userRoleAssignmentService;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private MembershipService membershipService;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private CustomerService customerService;
|
|
||||||
|
|
||||||
private SecurityContextMock securityContext;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void init() {
|
|
||||||
given(jsonParser.getCodec()).willReturn(codec);
|
|
||||||
|
|
||||||
given(ctx.getAutowireCapableBeanFactory()).willReturn(autowireCapableBeanFactory);
|
|
||||||
given(autowireCapableBeanFactory.createBean(MembershipService.class)).willReturn(membershipService);
|
|
||||||
given(autowireCapableBeanFactory.createBean(CustomerService.class)).willReturn(customerService);
|
|
||||||
given(customerService.findOne(1234L)).willReturn(
|
|
||||||
Optional.of(
|
|
||||||
new CustomerDTO()
|
|
||||||
.with(dto -> dto.setId(1234L))));
|
|
||||||
|
|
||||||
securityContext = SecurityContextMock.usingMock(userRoleAssignmentService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void adminShouldHaveRightToCreate() throws IOException {
|
public void shouldHaveProperAccessForAdmin() {
|
||||||
securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN);
|
initAccessFor(MembershipDTO.class, Role.ADMIN).shouldBeExactlyFor(
|
||||||
givenJSonTree(asJSon(ImmutablePair.of("customerId", 1234L)));
|
"admissionDocumentDate",
|
||||||
|
"cancellationDocumentDate",
|
||||||
// when
|
"memberFromDate",
|
||||||
final MembershipDTO actualDto = new JSonDeserializationWithAccessFilter<>(
|
"memberUntilDate",
|
||||||
ctx,
|
"customerId",
|
||||||
userRoleAssignmentService,
|
"remark");
|
||||||
jsonParser,
|
updateAccessFor(MembershipDTO.class, Role.ADMIN).shouldBeExactlyFor(
|
||||||
null,
|
"cancellationDocumentDate",
|
||||||
MembershipDTO.class)
|
"memberUntilDate",
|
||||||
.deserialize();
|
"remark");
|
||||||
|
readAccessFor(MembershipDTO.class, Role.ADMIN).shouldBeForAllFields();
|
||||||
// then
|
|
||||||
assertThat(actualDto.getCustomerId()).isEqualTo(1234L);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void contractualContactShouldNotHaveRightToCreate() throws IOException {
|
public void shouldHaveProperAccessForSupporter() {
|
||||||
securityContext.havingAuthenticatedUser().withRole(CustomerDTO.class, 1234L, Role.CONTRACTUAL_CONTACT);
|
initAccessFor(MembershipDTO.class, Role.SUPPORTER).shouldBeForNothing();
|
||||||
givenJSonTree(asJSon(ImmutablePair.of("customerId", 1234L)));
|
updateAccessFor(MembershipDTO.class, Role.SUPPORTER).shouldBeForNothing();
|
||||||
|
readAccessFor(MembershipDTO.class, Role.SUPPORTER).shouldBeForAllFields();
|
||||||
// when
|
|
||||||
Throwable exception = catchThrowable(
|
|
||||||
() -> new JSonDeserializationWithAccessFilter<>(
|
|
||||||
ctx,
|
|
||||||
userRoleAssignmentService,
|
|
||||||
jsonParser,
|
|
||||||
null,
|
|
||||||
MembershipDTO.class).deserialize());
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
|
||||||
assertThat(badRequestAlertException.getParam()).isEqualTo("MembershipDTO.customerId");
|
|
||||||
assertThat(badRequestAlertException.getErrorKey()).isEqualTo("referencingProhibited");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- only fixture code below ---
|
@Test
|
||||||
|
public void shouldHaveProperAccessForContractualContact() {
|
||||||
private void givenJSonTree(String givenJSon) throws IOException {
|
initAccessFor(MembershipDTO.class, Role.CONTRACTUAL_CONTACT).shouldBeForNothing();
|
||||||
given(codec.readTree(jsonParser)).willReturn(new ObjectMapper().readTree(givenJSon));
|
updateAccessFor(MembershipDTO.class, Role.CONTRACTUAL_CONTACT).shouldBeForNothing();
|
||||||
|
readAccessFor(MembershipDTO.class, Role.CONTRACTUAL_CONTACT).shouldBeExactlyFor(
|
||||||
|
"id",
|
||||||
|
"admissionDocumentDate",
|
||||||
|
"cancellationDocumentDate",
|
||||||
|
"memberFromDate",
|
||||||
|
"memberUntilDate",
|
||||||
|
"customerId",
|
||||||
|
"customerPrefix",
|
||||||
|
"customerDisplayLabel",
|
||||||
|
"displayLabel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldHaveNoAccessForTechnicalContact() {
|
||||||
|
initAccessFor(MembershipDTO.class, Role.TECHNICAL_CONTACT).shouldBeForNothing();
|
||||||
|
updateAccessFor(MembershipDTO.class, Role.TECHNICAL_CONTACT).shouldBeForNothing();
|
||||||
|
readAccessFor(MembershipDTO.class, Role.TECHNICAL_CONTACT).shouldBeForNothing();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldHaveNoAccessForNormalUsersWithinCustomerRealm() {
|
||||||
|
initAccessFor(MembershipDTO.class, Role.ANY_CUSTOMER_USER).shouldBeForNothing();
|
||||||
|
updateAccessFor(MembershipDTO.class, Role.ANY_CUSTOMER_USER).shouldBeForNothing();
|
||||||
|
readAccessFor(MembershipDTO.class, Role.ANY_CUSTOMER_USER).shouldBeForNothing();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- only test fixture below ---
|
||||||
|
|
||||||
|
public static MembershipDTO createSampleDTO(final Long id, final Long parentId) {
|
||||||
|
final MembershipDTO dto = new MembershipDTO();
|
||||||
|
dto.setId(id);
|
||||||
|
final LocalDate referenceDate = LocalDate.parse("2000-12-07");
|
||||||
|
dto.setAdmissionDocumentDate(referenceDate);
|
||||||
|
dto.setCancellationDocumentDate(referenceDate.plusDays(3500));
|
||||||
|
dto.setMemberFromDate(referenceDate.plusDays(4));
|
||||||
|
dto.setMemberUntilDate(referenceDate.plusDays(3500).plusDays(400).withDayOfYear(1).minusDays(1));
|
||||||
|
dto.setRemark("Some Remark");
|
||||||
|
dto.setCustomerId(parentId);
|
||||||
|
dto.setCustomerPrefix("abc");
|
||||||
|
dto.setCustomerDisplayLabel("ABC GmbH [abc:10001]");
|
||||||
|
dto.setDisplayLabel("ABC GmbH [abc:10001] 2000-12-11 - 2011-12-31");
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MembershipDTO createRandomDTO(final Long id, final Long parentId) {
|
||||||
|
final MembershipDTO dto = new MembershipDTO();
|
||||||
|
dto.setId(id);
|
||||||
|
final LocalDate randomDate = LocalDate.parse("2000-12-07").plusDays(RandomUtils.nextInt(1, 999));
|
||||||
|
dto.setAdmissionDocumentDate(randomDate);
|
||||||
|
dto.setCancellationDocumentDate(randomDate.plusDays(3500));
|
||||||
|
dto.setMemberFromDate(randomDate.plusDays(4));
|
||||||
|
dto.setMemberUntilDate(randomDate.plusDays(3500).plusDays(400).withDayOfYear(1).minusDays(1));
|
||||||
|
dto.setRemark(RandomStringUtils.randomAlphanumeric(20).toUpperCase());
|
||||||
|
dto.setCustomerId(parentId);
|
||||||
|
dto.setCustomerPrefix(RandomStringUtils.randomAlphabetic(3).toLowerCase());
|
||||||
|
dto.setCustomerDisplayLabel(RandomStringUtils.randomAlphabetic(13));
|
||||||
|
dto.setDisplayLabel(dto.getCustomerDisplayLabel() + dto.getMemberFromDate() + " - " + dto.getMemberUntilDate());
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,154 @@
|
|||||||
|
// Licensed under Apache-2.0
|
||||||
|
package org.hostsharing.hsadminng.service.dto;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.tuple.ImmutablePair.of;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
|
||||||
|
import org.hostsharing.hsadminng.domain.Customer;
|
||||||
|
import org.hostsharing.hsadminng.domain.User;
|
||||||
|
import org.hostsharing.hsadminng.domain.UserRoleAssignment;
|
||||||
|
import org.hostsharing.hsadminng.repository.UserRepository;
|
||||||
|
import org.hostsharing.hsadminng.repository.UserRoleAssignmentRepository;
|
||||||
|
import org.hostsharing.hsadminng.security.AuthoritiesConstants;
|
||||||
|
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
||||||
|
import org.hostsharing.hsadminng.service.accessfilter.JSonBuilder;
|
||||||
|
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||||
|
import org.hostsharing.hsadminng.service.accessfilter.SecurityContextMock;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.json.JsonTest;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@JsonTest
|
||||||
|
@SpringBootTest(
|
||||||
|
classes = {
|
||||||
|
UserRoleAssignmentRepository.class,
|
||||||
|
UserRoleAssignmentService.class,
|
||||||
|
UserRoleAssignment.UserRoleAssignmentJsonSerializer.class,
|
||||||
|
UserRoleAssignment.UserRoleAssignmentJsonDeserializer.class })
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
public class UserRoleAssignmentUnitTest {
|
||||||
|
|
||||||
|
public static final long USER_ROLE_ASSIGNMENT_ID = 1234L;
|
||||||
|
public static final long CUSTOMER_ID = 888L;
|
||||||
|
public static final long USER_ID = 42L;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public MockitoRule mockito = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private UserRepository userRepository;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private UserRoleAssignmentRepository userRoleAssignmentRepository;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private UserRoleAssignmentService userRoleAssignmentService;
|
||||||
|
|
||||||
|
private SecurityContextMock securityContext;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
securityContext = SecurityContextMock.usingMock(userRoleAssignmentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerializationAsContractualCustomerContact() throws JsonProcessingException {
|
||||||
|
|
||||||
|
// given
|
||||||
|
securityContext.havingAuthenticatedUser().withRole(CustomerDTO.class, CUSTOMER_ID, Role.CONTRACTUAL_CONTACT);
|
||||||
|
UserRoleAssignment given = createSomeUserRoleAssignment(USER_ROLE_ASSIGNMENT_ID);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String actual = objectMapper.writeValueAsString(given);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals("{}", actual); // dependent rights not yet implemented for UserRoleAssignments
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerializationAsSupporter() throws JsonProcessingException {
|
||||||
|
|
||||||
|
// given
|
||||||
|
securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.SUPPORTER);
|
||||||
|
UserRoleAssignment given = createSomeUserRoleAssignment(USER_ROLE_ASSIGNMENT_ID);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String actual = objectMapper.writeValueAsString(given);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(actual).isEqualTo(createExpectedJSon(given));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeserializeAsAdmin() throws IOException {
|
||||||
|
// given
|
||||||
|
securityContext.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN);
|
||||||
|
given(userRoleAssignmentRepository.findById(USER_ROLE_ASSIGNMENT_ID))
|
||||||
|
.willReturn(Optional.of(new UserRoleAssignment().id(USER_ROLE_ASSIGNMENT_ID)));
|
||||||
|
final User expectedUser = new User().id(USER_ID);
|
||||||
|
given(userRepository.getOne(USER_ID)).willReturn(expectedUser);
|
||||||
|
String json = JSonBuilder.asJSon(
|
||||||
|
of("id", USER_ROLE_ASSIGNMENT_ID),
|
||||||
|
of("entityTypeId", Customer.ENTITY_TYPE_ID),
|
||||||
|
of("entityObjectId", CUSTOMER_ID),
|
||||||
|
of(
|
||||||
|
"user",
|
||||||
|
JSonBuilder.asJSon(
|
||||||
|
of("id", USER_ID))),
|
||||||
|
of("assignedRole", Role.TECHNICAL_CONTACT.name()));
|
||||||
|
|
||||||
|
// when
|
||||||
|
UserRoleAssignment actual = objectMapper.readValue(json, UserRoleAssignment.class);
|
||||||
|
|
||||||
|
// then
|
||||||
|
UserRoleAssignment expected = new UserRoleAssignment();
|
||||||
|
expected.setId(USER_ROLE_ASSIGNMENT_ID);
|
||||||
|
expected.setEntityTypeId(Customer.ENTITY_TYPE_ID);
|
||||||
|
expected.setEntityObjectId(CUSTOMER_ID);
|
||||||
|
expected.setAssignedRole(Role.TECHNICAL_CONTACT);
|
||||||
|
expected.setUser(expectedUser);
|
||||||
|
assertThat(actual).isEqualToComparingFieldByField(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- only test fixture below ---
|
||||||
|
|
||||||
|
public static String createExpectedJSon(UserRoleAssignment dto) {
|
||||||
|
return new JSonBuilder()
|
||||||
|
.withFieldValueIfPresent("id", dto.getId())
|
||||||
|
.withFieldValueIfPresent("entityTypeId", dto.getEntityTypeId())
|
||||||
|
.withFieldValueIfPresent("entityObjectId", dto.getEntityObjectId())
|
||||||
|
.withFieldValueIfPresent("assignedRole", dto.getAssignedRole())
|
||||||
|
.withFieldValueIfPresent("user", dto.getUser().getId())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserRoleAssignment createSomeUserRoleAssignment(final Long id) {
|
||||||
|
final UserRoleAssignment given = new UserRoleAssignment();
|
||||||
|
given.setId(id);
|
||||||
|
given.setEntityTypeId(Customer.ENTITY_TYPE_ID);
|
||||||
|
given.setEntityObjectId(CUSTOMER_ID);
|
||||||
|
given.setUser(new User().id(USER_ID));
|
||||||
|
given.setAssignedRole(Role.TECHNICAL_CONTACT);
|
||||||
|
return given;
|
||||||
|
}
|
||||||
|
}
|
@ -180,6 +180,9 @@ public class MembershipResourceIntTest {
|
|||||||
|
|
||||||
// Create the Membership
|
// Create the Membership
|
||||||
MembershipDTO membershipDTO = membershipMapper.toDto(membership);
|
MembershipDTO membershipDTO = membershipMapper.toDto(membership);
|
||||||
|
membershipDTO.setCustomerPrefix(null);
|
||||||
|
membershipDTO.setCustomerDisplayLabel(null);
|
||||||
|
membershipDTO.setDisplayLabel(null);
|
||||||
restMembershipMockMvc.perform(
|
restMembershipMockMvc.perform(
|
||||||
post("/api/memberships")
|
post("/api/memberships")
|
||||||
.contentType(TestUtil.APPLICATION_JSON_UTF8)
|
.contentType(TestUtil.APPLICATION_JSON_UTF8)
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
// Licensed under Apache-2.0
|
||||||
|
package org.hostsharing.hsadminng.web.rest;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
|
import static org.assertj.core.api.ThrowableAssert.catchThrowable;
|
||||||
|
|
||||||
|
import org.hostsharing.hsadminng.service.dto.MembershipDTO;
|
||||||
|
import org.hostsharing.hsadminng.service.dto.MembershipDTOUnitTest;
|
||||||
|
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
// Currently this class tests mostly special 'bad paths'
|
||||||
|
// which make little sense to test in *ResourceIntTest.
|
||||||
|
public class MembershipResourceUnitTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public MockitoRule mockitoRule = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private MembershipResource membershipResource;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSepaMandateWithoutIdThrowsBadRequestException() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
final MembershipDTO givenDto = MembershipDTOUnitTest.createRandomDTO(null, 1L);
|
||||||
|
|
||||||
|
// when
|
||||||
|
final Throwable actual = catchThrowable(() -> membershipResource.updateMembership(givenDto));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(actual).isInstanceOfSatisfying(BadRequestAlertException.class, bre -> {
|
||||||
|
assertThat(bre.getErrorKey()).isEqualTo("idnull");
|
||||||
|
assertThat(bre.getParam()).isEqualTo("membership");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSepaMandateWithIdThrowsBadRequestException() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
final MembershipDTO givenDto = MembershipDTOUnitTest.createRandomDTO(2L, 1L);
|
||||||
|
|
||||||
|
// when
|
||||||
|
final Throwable actual = catchThrowable(() -> membershipResource.createMembership(givenDto));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(actual).isInstanceOfSatisfying(BadRequestAlertException.class, bre -> {
|
||||||
|
assertThat(bre.getErrorKey()).isEqualTo("idexists");
|
||||||
|
assertThat(bre.getParam()).isEqualTo("membership");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -11,9 +11,11 @@ import org.hostsharing.hsadminng.HsadminNgApp;
|
|||||||
import org.hostsharing.hsadminng.domain.User;
|
import org.hostsharing.hsadminng.domain.User;
|
||||||
import org.hostsharing.hsadminng.domain.UserRoleAssignment;
|
import org.hostsharing.hsadminng.domain.UserRoleAssignment;
|
||||||
import org.hostsharing.hsadminng.repository.UserRoleAssignmentRepository;
|
import org.hostsharing.hsadminng.repository.UserRoleAssignmentRepository;
|
||||||
|
import org.hostsharing.hsadminng.security.AuthoritiesConstants;
|
||||||
import org.hostsharing.hsadminng.service.UserRoleAssignmentQueryService;
|
import org.hostsharing.hsadminng.service.UserRoleAssignmentQueryService;
|
||||||
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
import org.hostsharing.hsadminng.service.UserRoleAssignmentService;
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||||
|
import org.hostsharing.hsadminng.service.accessfilter.SecurityContextFake;
|
||||||
import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator;
|
import org.hostsharing.hsadminng.web.rest.errors.ExceptionTranslator;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -94,6 +96,8 @@ public class UserRoleAssignmentResourceIntTest {
|
|||||||
.setMessageConverters(jacksonMessageConverter)
|
.setMessageConverters(jacksonMessageConverter)
|
||||||
.setValidator(validator)
|
.setValidator(validator)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
SecurityContextFake.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.SUPPORTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,9 +107,13 @@ public class UserRoleAssignmentResourceIntTest {
|
|||||||
* if they test an entity which requires the current entity.
|
* if they test an entity which requires the current entity.
|
||||||
*/
|
*/
|
||||||
public static UserRoleAssignment createEntity(EntityManager em) {
|
public static UserRoleAssignment createEntity(EntityManager em) {
|
||||||
|
User user = UserResourceIntTest.createEntity(em);
|
||||||
|
em.persist(user);
|
||||||
|
em.flush();
|
||||||
UserRoleAssignment userRoleAssignment = new UserRoleAssignment()
|
UserRoleAssignment userRoleAssignment = new UserRoleAssignment()
|
||||||
.entityTypeId(DEFAULT_ENTITY_TYPE_ID)
|
.entityTypeId(DEFAULT_ENTITY_TYPE_ID)
|
||||||
.entityObjectId(DEFAULT_ENTITY_OBJECT_ID)
|
.entityObjectId(DEFAULT_ENTITY_OBJECT_ID)
|
||||||
|
.user(user)
|
||||||
.assignedRole(DEFAULT_ASSIGNED_ROLE);
|
.assignedRole(DEFAULT_ASSIGNED_ROLE);
|
||||||
return userRoleAssignment;
|
return userRoleAssignment;
|
||||||
}
|
}
|
||||||
@ -121,6 +129,7 @@ public class UserRoleAssignmentResourceIntTest {
|
|||||||
int databaseSizeBeforeCreate = userRoleAssignmentRepository.findAll().size();
|
int databaseSizeBeforeCreate = userRoleAssignmentRepository.findAll().size();
|
||||||
|
|
||||||
// Create the UserRoleAssignment
|
// Create the UserRoleAssignment
|
||||||
|
SecurityContextFake.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN);
|
||||||
restUserRoleAssignmentMockMvc.perform(
|
restUserRoleAssignmentMockMvc.perform(
|
||||||
post("/api/user-role-assignments")
|
post("/api/user-role-assignments")
|
||||||
.contentType(TestUtil.APPLICATION_JSON_UTF8)
|
.contentType(TestUtil.APPLICATION_JSON_UTF8)
|
||||||
@ -460,6 +469,7 @@ public class UserRoleAssignmentResourceIntTest {
|
|||||||
int databaseSizeBeforeUpdate = userRoleAssignmentRepository.findAll().size();
|
int databaseSizeBeforeUpdate = userRoleAssignmentRepository.findAll().size();
|
||||||
|
|
||||||
// Update the userRoleAssignment
|
// Update the userRoleAssignment
|
||||||
|
SecurityContextFake.havingAuthenticatedUser().withAuthority(AuthoritiesConstants.ADMIN);
|
||||||
UserRoleAssignment updatedUserRoleAssignment = userRoleAssignmentRepository.findById(userRoleAssignment.getId()).get();
|
UserRoleAssignment updatedUserRoleAssignment = userRoleAssignmentRepository.findById(userRoleAssignment.getId()).get();
|
||||||
// Disconnect from session so that the updates on updatedUserRoleAssignment are not directly saved in db
|
// Disconnect from session so that the updates on updatedUserRoleAssignment are not directly saved in db
|
||||||
em.detach(updatedUserRoleAssignment);
|
em.detach(updatedUserRoleAssignment);
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
// Licensed under Apache-2.0
|
||||||
|
package org.hostsharing.hsadminng.web.rest;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
|
import static org.assertj.core.api.ThrowableAssert.catchThrowable;
|
||||||
|
|
||||||
|
import org.hostsharing.hsadminng.domain.UserRoleAssignment;
|
||||||
|
import org.hostsharing.hsadminng.service.dto.UserRoleAssignmentUnitTest;
|
||||||
|
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
|
// Currently this class tests mostly special 'bad paths'
|
||||||
|
// which make little sense to test in *ResourceIntTest.
|
||||||
|
public class UserRoleAssignmentResourceUnitTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public MockitoRule mockitoRule = MockitoJUnit.rule();
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private UserRoleAssignmentResource userRoleAssignmentResource;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createUserRoleAssignmentWithoutIdThrowsBadRequestException() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
final UserRoleAssignment givenEntity = UserRoleAssignmentUnitTest.createSomeUserRoleAssignment(null);
|
||||||
|
|
||||||
|
// when
|
||||||
|
final Throwable actual = catchThrowable(() -> userRoleAssignmentResource.updateUserRoleAssignment(givenEntity));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(actual).isInstanceOfSatisfying(BadRequestAlertException.class, bre -> {
|
||||||
|
assertThat(bre.getErrorKey()).isEqualTo("idnull");
|
||||||
|
assertThat(bre.getParam()).isEqualTo("userRoleAssignment");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createUserRoleAssignmentWithIdThrowsBadRequestException() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
final UserRoleAssignment givenEntity = UserRoleAssignmentUnitTest.createSomeUserRoleAssignment(1L);
|
||||||
|
|
||||||
|
// when
|
||||||
|
final Throwable actual = catchThrowable(() -> userRoleAssignmentResource.createUserRoleAssignment(givenEntity));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(actual).isInstanceOfSatisfying(BadRequestAlertException.class, bre -> {
|
||||||
|
assertThat(bre.getErrorKey()).isEqualTo("idexists");
|
||||||
|
assertThat(bre.getParam()).isEqualTo("userRoleAssignment");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user