merging current DTO during deserialization
This commit is contained in:
parent
fb961ca4a1
commit
3e30cf2d17
@ -80,7 +80,6 @@ public final class SecurityUtils {
|
||||
}
|
||||
|
||||
public static Role getLoginUserRoleFor(final Class<?> onDtoClass, final Long onId) {
|
||||
|
||||
final Role highestRole = userRoleAssignments.stream().
|
||||
map(ura ->
|
||||
matches(onDtoClass, onId, ura)
|
||||
@ -91,7 +90,7 @@ public final class SecurityUtils {
|
||||
}
|
||||
|
||||
private static boolean matches(Class<?> onDtoClass, Long onId, UserRoleAssignment ura) {
|
||||
final boolean matches = (ura.onClass == null || onDtoClass == ura.onClass) && (ura.onId == null || onId.equals(ura.onId) );
|
||||
final boolean matches = (ura.onClass == null || onDtoClass == ura.onClass) && (ura.onId == null || ura.onId.equals(onId));
|
||||
return matches;
|
||||
}
|
||||
|
||||
|
@ -3,5 +3,5 @@ package org.hostsharing.hsadminng.service;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface IdToDtoResolver<T> {
|
||||
Optional<? extends T> findOne(Long id);
|
||||
Optional<T> findOne(Long id);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import java.util.Optional;
|
||||
*/
|
||||
@Service
|
||||
@Transactional
|
||||
public class ShareService {
|
||||
public class ShareService implements IdToDtoResolver<ShareDTO> {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(ShareService.class);
|
||||
|
||||
|
@ -4,14 +4,17 @@ import org.hostsharing.hsadminng.security.SecurityUtils;
|
||||
import org.hostsharing.hsadminng.service.IdToDtoResolver;
|
||||
import org.hostsharing.hsadminng.service.dto.MembershipDTO;
|
||||
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
||||
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
|
||||
abstract class JSonAccessFilter<T> {
|
||||
private final ApplicationContext ctx;
|
||||
final T dto;
|
||||
@ -72,7 +75,7 @@ abstract class JSonAccessFilter<T> {
|
||||
final Long parentId = (Long) ReflectionUtil.getValue(dto, parentIdField);
|
||||
final Role roleOnParent = SecurityUtils.getLoginUserRoleFor(parentDtoClass, parentId);
|
||||
|
||||
final Object parentEntity = findParentDto(parentDtoLoader, parentId);
|
||||
final Object parentEntity = loadDto(parentDtoLoader, parentId);
|
||||
return Role.broadest(baseRole, getLoginUserRoleOnAncestorOfDtoClassIfHigher(roleOnParent, parentEntity));
|
||||
}
|
||||
|
||||
@ -91,9 +94,16 @@ abstract class JSonAccessFilter<T> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object findParentDto(final Class<? extends IdToDtoResolver> parentDtoLoader, final Long parentId) {
|
||||
final IdToDtoResolver<MembershipDTO> idToDtoResolver = ctx.getAutowireCapableBeanFactory().createBean(parentDtoLoader);
|
||||
return idToDtoResolver.findOne(parentId).orElseThrow(() -> new EntityNotFoundException("Can't resolve parent entity ID " + parentId + " via " + parentDtoLoader));
|
||||
protected Object loadDto(final Class<? extends IdToDtoResolver> resolverClass, final Long id) {
|
||||
verify(id != null, "id must not be null");
|
||||
|
||||
final AutowireCapableBeanFactory beanFactory = ctx.getAutowireCapableBeanFactory();
|
||||
verify(beanFactory != null, "no bean factory found, probably missing mock configuration for ApplicationContext, e.g. given(...)");
|
||||
|
||||
final IdToDtoResolver<MembershipDTO> resolverBean = beanFactory.createBean(resolverClass);
|
||||
verify(resolverBean != null, "no " + resolverClass.getSimpleName() + " bean created, probably missing mock configuration for AutowireCapableBeanFactory, e.g. given(...)");
|
||||
|
||||
return resolverBean.findOne(id).orElseThrow(() -> new BadRequestAlertException("Can't resolve entity ID " + id + " via " + resolverClass, resolverClass.getSimpleName(), "isNotFound"));
|
||||
}
|
||||
|
||||
private static Field determineFieldWithAnnotation(final Class<?> dtoClass, final Class<? extends Annotation> idAnnotationClass) {
|
||||
|
@ -3,15 +3,18 @@ package org.hostsharing.hsadminng.service.accessfilter;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.TreeNode;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.IntNode;
|
||||
import com.fasterxml.jackson.databind.node.LongNode;
|
||||
import com.fasterxml.jackson.databind.node.TextNode;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
||||
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@ -20,7 +23,7 @@ import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked;
|
||||
public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
|
||||
private final TreeNode treeNode;
|
||||
private final Set<Field> modifiedFields = new HashSet<>();
|
||||
private final Set<Field> writtenFields = new HashSet<>();
|
||||
|
||||
public JSonDeserializerWithAccessFilter(final ApplicationContext ctx, final JsonParser jsonParser, final DeserializationContext deserializationContext, Class<T> dtoClass) {
|
||||
super(ctx, unchecked(dtoClass::newInstance));
|
||||
@ -30,7 +33,9 @@ public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
// Jackson deserializes from the JsonParser, thus no input parameter needed.
|
||||
public T deserialize() {
|
||||
deserializeValues();
|
||||
checkAccessToModifiedFields();
|
||||
final T currentDto = loadCurrentDto(getId());
|
||||
overwriteUnmodifiedFieldsWithCurrentValues(currentDto);
|
||||
checkAccessToWrittenFields(currentDto);
|
||||
return dto;
|
||||
}
|
||||
|
||||
@ -38,25 +43,52 @@ public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
treeNode.fieldNames().forEachRemaining(fieldName -> {
|
||||
try {
|
||||
final Field field = dto.getClass().getDeclaredField(fieldName);
|
||||
final Object value = readValue(treeNode, field);
|
||||
writeValue(dto, field, value);
|
||||
markAsModified(field);
|
||||
final Object newValue = readValue(treeNode, field);
|
||||
writeValue(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 Object currentDto) {
|
||||
if ( currentDto == null ) {
|
||||
return;
|
||||
}
|
||||
for (Field field : currentDto.getClass().getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(AccessFor.class) && !writtenFields.contains(field)) {
|
||||
final Object value = ReflectionUtil.getValue(currentDto, field);
|
||||
ReflectionUtil.setValue(dto, field, value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Object readValue(final TreeNode treeNode, final Field field) {
|
||||
final TreeNode fieldNode = treeNode.get(field.getName());
|
||||
return readValue(treeNode, field.getName(), field.getType());
|
||||
|
||||
}
|
||||
|
||||
private Object readValue(final TreeNode treeNode, final String fieldName, final Class<?> fieldClass) {
|
||||
final TreeNode fieldNode = treeNode.get(fieldName);
|
||||
if (fieldNode instanceof TextNode) {
|
||||
return ((TextNode) fieldNode).asText();
|
||||
} else if (fieldNode instanceof IntNode) {
|
||||
return ((IntNode) fieldNode).asInt();
|
||||
} else if (fieldNode instanceof LongNode) {
|
||||
return ((LongNode) fieldNode).asLong();
|
||||
} else 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());
|
||||
} else {
|
||||
throw new NotImplementedException("property type not yet implemented: " + field);
|
||||
throw new NotImplementedException("property type not yet implemented: " + fieldNode + " -> " + fieldName + ": " + fieldClass);
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,30 +99,36 @@ public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
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 (field.getType().isEnum()) {
|
||||
ReflectionUtil.setValue(dto, field, Enum.valueOf((Class<Enum>) field.getType(), value.toString()));
|
||||
} 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);
|
||||
}
|
||||
writtenFields.add(field);
|
||||
}
|
||||
|
||||
private void markAsModified(final Field field) {
|
||||
modifiedFields.add(field);
|
||||
}
|
||||
|
||||
private void checkAccessToModifiedFields() {
|
||||
modifiedFields.forEach(field -> {
|
||||
if ( !field.equals(selfIdField) ) {
|
||||
private void checkAccessToWrittenFields(final T currentDto) {
|
||||
writtenFields.forEach(field -> {
|
||||
if (!field.equals(selfIdField)) {
|
||||
final Role role = getLoginUserRole();
|
||||
if (getId() == null) {
|
||||
if (!getLoginUserRole().isAllowedToInit(field)) {
|
||||
if ( !field.equals(parentIdField)) {
|
||||
throw new BadRequestAlertException("Initialization of field prohibited for current user", toDisplay(field), "initializationProhibited");
|
||||
if (!role.isAllowedToInit(field)) {
|
||||
if (!field.equals(parentIdField)) {
|
||||
throw new BadRequestAlertException("Initialization of field " + toDisplay(field) + " prohibited for current user role " + role, toDisplay(field), "initializationProhibited");
|
||||
} else {
|
||||
throw new BadRequestAlertException("Referencing field prohibited for current user", toDisplay(field), "referencingProhibited");
|
||||
throw new BadRequestAlertException("Referencing field " + toDisplay(field) + " prohibited for current user role " + role, toDisplay(field), "referencingProhibited");
|
||||
}
|
||||
}
|
||||
} else if (!getLoginUserRole().isAllowedToUpdate(field)) {
|
||||
throw new BadRequestAlertException("Update of field prohibited for current user", toDisplay(field), "updateProhibited");
|
||||
} else if (isUpdate(field, dto, currentDto) && !getLoginUserRole().isAllowedToUpdate(field)){
|
||||
throw new BadRequestAlertException("Update of field " + toDisplay(field) + " prohibited for current user role " + role, toDisplay(field), "updateProhibited");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isUpdate(final Field field, final T dto, T currentDto) {
|
||||
return ObjectUtils.notEqual(ReflectionUtil.getValue(dto, field), ReflectionUtil.getValue(currentDto, field));
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class JSonSerializerWithAccessFilter <T> extends JSonAccessFilter<T> {
|
||||
public class JSonSerializerWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
private final JsonGenerator jsonGenerator;
|
||||
private final SerializerProvider serializerProvider;
|
||||
|
||||
@ -27,32 +27,35 @@ public class JSonSerializerWithAccessFilter <T> extends JSonAccessFilter<T> {
|
||||
public void serialize() throws IOException {
|
||||
|
||||
jsonGenerator.writeStartObject();
|
||||
for (Field prop : dto.getClass().getDeclaredFields()) {
|
||||
toJSon(dto, jsonGenerator, prop);
|
||||
for (Field field : dto.getClass().getDeclaredFields()) {
|
||||
toJSon(dto, jsonGenerator, field);
|
||||
}
|
||||
jsonGenerator.writeEndObject();
|
||||
}
|
||||
|
||||
private void toJSon(final Object dto, final JsonGenerator jsonGenerator, final Field prop) throws IOException {
|
||||
if (getLoginUserRole().isAllowedToRead(prop)) {
|
||||
final String fieldName = prop.getName();
|
||||
private void toJSon(final Object dto, final JsonGenerator jsonGenerator, final Field field) throws IOException {
|
||||
if (getLoginUserRole().isAllowedToRead(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?
|
||||
if (Integer.class.isAssignableFrom(prop.getType()) || int.class.isAssignableFrom(prop.getType())) {
|
||||
jsonGenerator.writeNumberField(fieldName, (int) get(dto, prop));
|
||||
} else if (Long.class.isAssignableFrom(prop.getType()) || long.class.isAssignableFrom(prop.getType())) {
|
||||
jsonGenerator.writeNumberField(fieldName, (long) get(dto, prop));
|
||||
} else if (LocalDate.class.isAssignableFrom(prop.getType())) {
|
||||
jsonGenerator.writeStringField(fieldName, get(dto, prop).toString()); // TODO proper format
|
||||
} else if (Enum.class.isAssignableFrom(prop.getType())) {
|
||||
jsonGenerator.writeStringField(fieldName, get(dto, prop).toString()); // TODO proper representation
|
||||
} else if (String.class.isAssignableFrom(prop.getType())) {
|
||||
jsonGenerator.writeStringField(fieldName, (String) get(dto, prop));
|
||||
final Object fieldValue = get(dto, field);
|
||||
if (fieldValue == null) {
|
||||
jsonGenerator.writeNullField(fieldName);
|
||||
} 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()); // TODO proper format
|
||||
} else if (Enum.class.isAssignableFrom(field.getType())) {
|
||||
jsonGenerator.writeStringField(fieldName, fieldValue.toString()); // TODO proper representation
|
||||
} else if (String.class.isAssignableFrom(field.getType())) {
|
||||
jsonGenerator.writeStringField(fieldName, (String) fieldValue);
|
||||
} else {
|
||||
throw new NotImplementedException("property type not yet implemented: " + prop);
|
||||
throw new NotImplementedException("property type not yet implemented: " + field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,17 +33,12 @@ public enum Role {
|
||||
*/
|
||||
SUPPORTER(3),
|
||||
|
||||
/**
|
||||
* This meta-role is to specify that any kind of customer contact can get access to the resource.
|
||||
*/
|
||||
ANY_CUSTOMER_CONTACT(20),
|
||||
|
||||
/**
|
||||
* This role is for contractual contacts of a customer, like a director of the company.
|
||||
* Who has this role, has the broadest access to all resources which belong to this customer.
|
||||
* Everything which relates to the contract with the customer, needs this role.
|
||||
*/
|
||||
CONTRACTUAL_CONTACT(21),
|
||||
CONTRACTUAL_CONTACT(20),
|
||||
|
||||
/**
|
||||
* This role is for financial contacts of a customer, e.g. for accessing billing data.
|
||||
@ -64,6 +59,11 @@ public enum Role {
|
||||
TECHNICAL_CONTACT(22),
|
||||
|
||||
|
||||
/**
|
||||
* This meta-role is to specify that any kind of customer contact can get access to the resource.
|
||||
*/
|
||||
ANY_CUSTOMER_CONTACT(29),
|
||||
|
||||
/**
|
||||
* Any user which belongs to a customer has at least this role.
|
||||
*/
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.hostsharing.hsadminng.service.accessfilter;
|
||||
|
||||
import org.hostsharing.hsadminng.service.IdToDtoResolver;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
@ -14,4 +16,6 @@ import java.lang.annotation.*;
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SelfId {
|
||||
/// The service which can load the referenced DTO.
|
||||
Class<? extends IdToDtoResolver<?>> resolver();
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public class AssetDTO implements Serializable {
|
||||
|
||||
private Long membershipId;
|
||||
|
||||
private String membershipAdmissionDocumentDate;
|
||||
private String membershipDisplayReference;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
@ -92,12 +92,12 @@ public class AssetDTO implements Serializable {
|
||||
this.membershipId = membershipId;
|
||||
}
|
||||
|
||||
public String getMembershipAdmissionDocumentDate() {
|
||||
return membershipAdmissionDocumentDate;
|
||||
public String getMembershipDisplayReference() {
|
||||
return membershipDisplayReference;
|
||||
}
|
||||
|
||||
public void setMembershipAdmissionDocumentDate(String membershipAdmissionDocumentDate) {
|
||||
this.membershipAdmissionDocumentDate = membershipAdmissionDocumentDate;
|
||||
public void setMembershipDisplayReference(String membershipDisplayReference) {
|
||||
this.membershipDisplayReference = membershipDisplayReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -131,7 +131,7 @@ public class AssetDTO implements Serializable {
|
||||
", amount=" + getAmount() +
|
||||
", remark='" + getRemark() + "'" +
|
||||
", membership=" + getMembershipId() +
|
||||
", membership='" + getMembershipAdmissionDocumentDate() + "'" +
|
||||
", membership='" + getMembershipDisplayReference() + "'" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import org.hostsharing.hsadminng.domain.enumeration.CustomerKind;
|
||||
import org.hostsharing.hsadminng.domain.enumeration.VatRegion;
|
||||
import org.hostsharing.hsadminng.service.CustomerService;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.*;
|
||||
import org.springframework.boot.jackson.JsonComponent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@ -21,9 +22,9 @@ import java.util.Objects;
|
||||
/**
|
||||
* A DTO for the Customer entity.
|
||||
*/
|
||||
public class CustomerDTO implements Serializable {
|
||||
public class CustomerDTO extends FluentBuilder<CustomerDTO> implements Serializable {
|
||||
|
||||
@SelfId
|
||||
@SelfId(resolver = CustomerService.class)
|
||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||
private Long id;
|
||||
|
||||
@ -41,31 +42,38 @@ public class CustomerDTO implements Serializable {
|
||||
|
||||
@NotNull
|
||||
@Size(max = 80)
|
||||
@AccessFor(init = Role.ADMIN, read = Role.ANY_CUSTOMER_USER)
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = Role.ANY_CUSTOMER_USER)
|
||||
private String name;
|
||||
|
||||
@NotNull
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = Role.CONTRACTUAL_CONTACT)
|
||||
private CustomerKind kind;
|
||||
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private LocalDate birthDate;
|
||||
|
||||
@Size(max = 80)
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private String birthPlace;
|
||||
|
||||
@Size(max = 80)
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private String registrationCourt;
|
||||
|
||||
@Size(max = 80)
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private String registrationNumber;
|
||||
|
||||
@NotNull
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private VatRegion vatRegion;
|
||||
|
||||
@Size(max = 40)
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private String vatNumber;
|
||||
|
||||
@Size(max = 80)
|
||||
@AccessFor(init = Role.ADMIN, update = Role.CONTRACTUAL_CONTACT, read = Role.ANY_CUSTOMER_CONTACT)
|
||||
@AccessFor(init = Role.ADMIN, update = Role.CONTRACTUAL_CONTACT, read = Role.CONTRACTUAL_CONTACT)
|
||||
private String contractualSalutation;
|
||||
|
||||
@NotNull
|
||||
@ -78,7 +86,7 @@ public class CustomerDTO implements Serializable {
|
||||
private String billingSalutation;
|
||||
|
||||
@Size(max = 400)
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = Role.CONTRACTUAL_CONTACT)
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private String billingAddress;
|
||||
|
||||
@Size(max = 160)
|
||||
|
@ -0,0 +1,15 @@
|
||||
package org.hostsharing.hsadminng.service.dto;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class FluentBuilder<T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T with(
|
||||
Consumer<T> builderFunction) {
|
||||
builderFunction.accept((T) this);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package org.hostsharing.hsadminng.service.dto;
|
||||
|
||||
import org.hostsharing.hsadminng.service.CustomerService;
|
||||
import org.hostsharing.hsadminng.service.MembershipService;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.AccessFor;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.ParentId;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||
@ -11,14 +12,13 @@ import javax.validation.constraints.Size;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A DTO for the Membership entity.
|
||||
*/
|
||||
public class MembershipDTO implements Serializable {
|
||||
public class MembershipDTO extends FluentBuilder<MembershipDTO> implements Serializable {
|
||||
|
||||
@SelfId
|
||||
@SelfId(resolver = MembershipService.class)
|
||||
@AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private Long id;
|
||||
|
||||
@ -47,12 +47,6 @@ public class MembershipDTO implements Serializable {
|
||||
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private String customerPrefix;
|
||||
|
||||
public MembershipDTO with(
|
||||
Consumer<MembershipDTO> builderFunction) {
|
||||
builderFunction.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
@ -1,5 +1,13 @@
|
||||
package org.hostsharing.hsadminng.service.dto;
|
||||
|
||||
import org.hostsharing.hsadminng.domain.enumeration.ShareAction;
|
||||
import org.hostsharing.hsadminng.service.MembershipService;
|
||||
import org.hostsharing.hsadminng.service.ShareService;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.AccessFor;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.ParentId;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
||||
import org.hostsharing.hsadminng.service.accessfilter.SelfId;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.io.Serializable;
|
||||
@ -11,18 +19,16 @@ import java.util.Objects;
|
||||
*/
|
||||
public class ShareDTO implements Serializable {
|
||||
|
||||
@SelfId
|
||||
@SelfId(resolver = ShareService.class)
|
||||
@AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private Long id;
|
||||
|
||||
@NotNull
|
||||
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private LocalDate documentDate;
|
||||
|
||||
@NotNull
|
||||
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private LocalDate valueDate;
|
||||
|
||||
@NotNull
|
||||
@ -41,8 +47,8 @@ public class ShareDTO implements Serializable {
|
||||
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private Long membershipId;
|
||||
|
||||
@AccessFor(init = Role.ADMIN, read = Role.SUPPORTER)
|
||||
private String membershipAdmissionDocumentDate;
|
||||
@AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private String membershipDisplayReference;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
@ -100,12 +106,12 @@ public class ShareDTO implements Serializable {
|
||||
this.membershipId = membershipId;
|
||||
}
|
||||
|
||||
public String getMembershipAdmissionDocumentDate() {
|
||||
return membershipAdmissionDocumentDate;
|
||||
public String getMembershipDisplayReference() {
|
||||
return membershipDisplayReference;
|
||||
}
|
||||
|
||||
public void setMembershipAdmissionDocumentDate(String membershipAdmissionDocumentDate) {
|
||||
this.membershipAdmissionDocumentDate = membershipAdmissionDocumentDate;
|
||||
public void setMembershipDisplayReference(String membershipDisplayReference) {
|
||||
this.membershipDisplayReference = membershipDisplayReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -139,7 +145,7 @@ public class ShareDTO implements Serializable {
|
||||
", quantity=" + getQuantity() +
|
||||
", remark='" + getRemark() + "'" +
|
||||
", membership=" + getMembershipId() +
|
||||
", membership='" + getMembershipAdmissionDocumentDate() + "'" +
|
||||
", membership='" + getMembershipDisplayReference() + "'" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import org.mapstruct.Mapping;
|
||||
public interface AssetMapper extends EntityMapper<AssetDTO, Asset> {
|
||||
|
||||
@Mapping(source = "membership.id", target = "membershipId")
|
||||
@Mapping(source = "membership.admissionDocumentDate", target = "membershipAdmissionDocumentDate")
|
||||
@Mapping(source = "membership.admissionDocumentDate", target = "membershipDisplayReference")
|
||||
AssetDTO toDto(Asset asset);
|
||||
|
||||
@Mapping(source = "membershipId", target = "membership")
|
||||
|
@ -12,7 +12,7 @@ import org.mapstruct.Mapping;
|
||||
public interface ShareMapper extends EntityMapper<ShareDTO, Share> {
|
||||
|
||||
@Mapping(source = "membership.id", target = "membershipId")
|
||||
@Mapping(source = "membership.admissionDocumentDate", target = "membershipAdmissionDocumentDate")
|
||||
@Mapping(source = "membership.admissionDocumentDate", target = "membershipDisplayReference")
|
||||
ShareDTO toDto(Share share);
|
||||
|
||||
@Mapping(source = "membershipId", target = "membership")
|
||||
|
@ -1,25 +1,24 @@
|
||||
package org.hostsharing.hsadminng.web.rest;
|
||||
|
||||
import io.github.jhipster.web.util.ResponseUtil;
|
||||
import org.hostsharing.hsadminng.service.CustomerQueryService;
|
||||
import org.hostsharing.hsadminng.service.CustomerService;
|
||||
import org.hostsharing.hsadminng.service.dto.CustomerCriteria;
|
||||
import org.hostsharing.hsadminng.service.dto.CustomerDTO;
|
||||
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||
import org.hostsharing.hsadminng.web.rest.util.HeaderUtil;
|
||||
import org.hostsharing.hsadminng.web.rest.util.PaginationUtil;
|
||||
import org.hostsharing.hsadminng.service.dto.CustomerDTO;
|
||||
import org.hostsharing.hsadminng.service.dto.CustomerCriteria;
|
||||
import org.hostsharing.hsadminng.service.CustomerQueryService;
|
||||
import io.github.jhipster.web.util.ResponseUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -72,7 +71,7 @@ public class CustomerResource {
|
||||
* @throws URISyntaxException if the Location URI syntax is incorrect
|
||||
*/
|
||||
@PutMapping("/customers")
|
||||
public ResponseEntity<CustomerDTO> updateCustomer(@Valid @RequestBody CustomerDTO customerDTO) throws URISyntaxException {
|
||||
public ResponseEntity<CustomerDTO> updateCustomer(@RequestBody CustomerDTO customerDTO) {
|
||||
log.debug("REST request to update Customer : {}", customerDTO);
|
||||
if (customerDTO.getId() == null) {
|
||||
throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
|
||||
|
@ -28,7 +28,7 @@
|
||||
<dt><span jhiTranslate="hsadminNgApp.asset.membership">Membership</span></dt>
|
||||
<dd>
|
||||
<div *ngIf="asset.membershipId">
|
||||
<a [routerLink]="['/membership', asset.membershipId, 'view']">{{asset.membershipAdmissionDocumentDate}}</a>
|
||||
<a [routerLink]="['/membership', asset.membershipId, 'view']">{{asset.membershipDisplayReference}}</a>
|
||||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<th jhiSortBy="action"><span jhiTranslate="hsadminNgApp.asset.action">Action</span> <fa-icon [icon]="'sort'"></fa-icon></th>
|
||||
<th jhiSortBy="amount"><span jhiTranslate="hsadminNgApp.asset.amount">Amount</span> <fa-icon [icon]="'sort'"></fa-icon></th>
|
||||
<th jhiSortBy="remark"><span jhiTranslate="hsadminNgApp.asset.remark">Remark</span> <fa-icon [icon]="'sort'"></fa-icon></th>
|
||||
<th jhiSortBy="membershipAdmissionDocumentDate"><span jhiTranslate="hsadminNgApp.asset.membership">Membership</span> <fa-icon [icon]="'sort'"></fa-icon></th>
|
||||
<th jhiSortBy="membershipDisplayReference"><span jhiTranslate="hsadminNgApp.asset.membership">Membership</span> <fa-icon [icon]="'sort'"></fa-icon></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -34,7 +34,7 @@
|
||||
<td>{{asset.remark}}</td>
|
||||
<td>
|
||||
<div *ngIf="asset.membershipId">
|
||||
<a [routerLink]="['../membership', asset.membershipId , 'view' ]" >{{asset.membershipAdmissionDocumentDate}}</a>
|
||||
<a [routerLink]="['../membership', asset.membershipId , 'view' ]" >{{asset.membershipDisplayReference}}</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
|
@ -28,7 +28,7 @@
|
||||
<dt><span jhiTranslate="hsadminNgApp.share.membership">Membership</span></dt>
|
||||
<dd>
|
||||
<div *ngIf="share.membershipId">
|
||||
<a [routerLink]="['/membership', share.membershipId, 'view']">{{share.membershipAdmissionDocumentDate}}</a>
|
||||
<a [routerLink]="['/membership', share.membershipId, 'view']">{{share.membershipDisplayReference}}</a>
|
||||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<th jhiSortBy="action"><span jhiTranslate="hsadminNgApp.share.action">Action</span> <fa-icon [icon]="'sort'"></fa-icon></th>
|
||||
<th jhiSortBy="quantity"><span jhiTranslate="hsadminNgApp.share.quantity">Quantity</span> <fa-icon [icon]="'sort'"></fa-icon></th>
|
||||
<th jhiSortBy="remark"><span jhiTranslate="hsadminNgApp.share.remark">Remark</span> <fa-icon [icon]="'sort'"></fa-icon></th>
|
||||
<th jhiSortBy="membershipAdmissionDocumentDate"><span jhiTranslate="hsadminNgApp.share.membership">Membership</span> <fa-icon [icon]="'sort'"></fa-icon></th>
|
||||
<th jhiSortBy="membershipDisplayReference"><span jhiTranslate="hsadminNgApp.share.membership">Membership</span> <fa-icon [icon]="'sort'"></fa-icon></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -34,7 +34,7 @@
|
||||
<td>{{share.remark}}</td>
|
||||
<td>
|
||||
<div *ngIf="share.membershipId">
|
||||
<a [routerLink]="['../membership', share.membershipId , 'view' ]" >{{share.membershipAdmissionDocumentDate}}</a>
|
||||
<a [routerLink]="['../membership', share.membershipId , 'view' ]" >{{share.membershipDisplayReference}}</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
|
@ -16,7 +16,7 @@ export interface IAsset {
|
||||
action?: AssetAction;
|
||||
amount?: number;
|
||||
remark?: string;
|
||||
membershipAdmissionDocumentDate?: string;
|
||||
membershipDisplayReference?: string;
|
||||
membershipId?: number;
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ export class Asset implements IAsset {
|
||||
public action?: AssetAction,
|
||||
public amount?: number,
|
||||
public remark?: string,
|
||||
public membershipAdmissionDocumentDate?: string,
|
||||
public membershipDisplayReference?: string,
|
||||
public membershipId?: number
|
||||
) {}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ export interface IShare {
|
||||
action?: ShareAction;
|
||||
quantity?: number;
|
||||
remark?: string;
|
||||
membershipAdmissionDocumentDate?: string;
|
||||
membershipDisplayReference?: string;
|
||||
membershipId?: number;
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ export class Share implements IShare {
|
||||
public action?: ShareAction,
|
||||
public quantity?: number,
|
||||
public remark?: string,
|
||||
public membershipAdmissionDocumentDate?: string,
|
||||
public membershipDisplayReference?: string,
|
||||
public membershipId?: number
|
||||
) {}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"error": {
|
||||
"idNotFound": "Technische Datensatz-ID nicht gefunden",
|
||||
"shareSubscriptionPositiveQuantity": "Zeichnungen von Geschäftsanteilen erfordern eine positive Stückzahl",
|
||||
"shareCancellationNegativeQuantity": "Kündigungen von Geschäftsanteilen erfordern eine negative Stückzahl",
|
||||
"shareTransactionImmutable": "Transaktionen mit Geschäftsanteilen sind unveränderlich",
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"error": {
|
||||
"idNotFound": "Technical record-ID not found",
|
||||
"shareSubscriptionPositiveQuantity": "Share subscriptions require a positive quantity",
|
||||
"shareCancellationNegativeQuantity": "Share cancellations require a negative quantity",
|
||||
"shareTransactionImmutable": "Share transactions are immutable",
|
||||
|
@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.TreeNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.hostsharing.hsadminng.service.IdToDtoResolver;
|
||||
import org.hostsharing.hsadminng.service.dto.FluentBuilder;
|
||||
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@ -13,9 +14,11 @@ 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.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||
@ -31,22 +34,41 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
public MockitoRule mockitoRule = MockitoJUnit.rule();
|
||||
|
||||
@Mock
|
||||
public ApplicationContext ctx;
|
||||
private ApplicationContext ctx;
|
||||
|
||||
@Mock
|
||||
public JsonParser jsonParser;
|
||||
private AutowireCapableBeanFactory autowireCapableBeanFactory;
|
||||
|
||||
@Mock
|
||||
public ObjectCodec codec;
|
||||
private JsonParser jsonParser;
|
||||
|
||||
@Mock
|
||||
public TreeNode treeNode;
|
||||
private ObjectCodec codec;
|
||||
|
||||
@Mock
|
||||
private TreeNode treeNode;
|
||||
|
||||
@Mock
|
||||
private GivenService givenService;
|
||||
|
||||
@Mock
|
||||
private GivenChildService givenChildService;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(GivenDto.class, 1234L, Role.ACTUAL_CUSTOMER_USER);
|
||||
|
||||
given (ctx.getAutowireCapableBeanFactory()).willReturn(autowireCapableBeanFactory);
|
||||
given(autowireCapableBeanFactory.createBean(GivenService.class)).willReturn(givenService);
|
||||
given(givenService.findOne(1234L)).willReturn(Optional.of(new GivenDto()
|
||||
.with(dto -> dto.id = 1234L)
|
||||
.with(dto -> dto.openIntegerField = 1)
|
||||
.with(dto -> dto.openLongField = 2L)
|
||||
.with(dto -> dto.openStringField = "3")
|
||||
.with(dto -> dto.restrictedField = "initial value of restricted field")
|
||||
));
|
||||
|
||||
given(jsonParser.getCodec()).willReturn(codec);
|
||||
}
|
||||
|
||||
@ -99,21 +121,54 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
givenUserHavingRole(GivenDto.class, 1234L, Role.FINANCIAL_CONTACT);
|
||||
givenJSonTree(asJSon(
|
||||
ImmutablePair.of("id", 1234L),
|
||||
ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||
ImmutablePair.of("restrictedField", "update value of restricted field")));
|
||||
|
||||
// when
|
||||
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize();
|
||||
|
||||
// then
|
||||
assertThat(actualDto.restrictedField).isEqualTo("Restricted String Value");
|
||||
assertThat(actualDto.restrictedField).isEqualTo("update value of restricted field");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeserializeUnchangedStringFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
|
||||
// given
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(GivenDto.class, 1234L, Role.ANY_CUSTOMER_USER);
|
||||
givenJSonTree(asJSon(
|
||||
ImmutablePair.of("id", 1234L),
|
||||
ImmutablePair.of("restrictedField", "initial value of restricted field")));
|
||||
|
||||
// when
|
||||
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize();
|
||||
|
||||
// then
|
||||
assertThat(actualDto.restrictedField).isEqualTo("initial value of restricted field");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotDeserializeUpatedStringFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
|
||||
// given
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(GivenDto.class, 1L, Role.ANY_CUSTOMER_USER);
|
||||
givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "updated value of restricted field")));
|
||||
|
||||
// when
|
||||
Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize());
|
||||
|
||||
// then
|
||||
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||
assertThat(badRequestAlertException.getParam()).isEqualTo("GivenDto.restrictedField");
|
||||
assertThat(badRequestAlertException.getErrorKey()).isEqualTo("initializationProhibited");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldInitializeFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
|
||||
// given
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(null, null, Role.ANY_CUSTOMER_USER);
|
||||
givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||
givenUserHavingRole(GivenDto.class, 1L, Role.ANY_CUSTOMER_USER);
|
||||
givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "another value of restricted field")));
|
||||
|
||||
// when
|
||||
Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenDto.class).deserialize());
|
||||
@ -130,7 +185,7 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
// given
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(GivenDto.class, 9999L, Role.CONTRACTUAL_CONTACT);
|
||||
givenJSonTree(asJSon(ImmutablePair.of("parentId", 1111L)));
|
||||
givenJSonTree(asJSon(ImmutablePair.of("parentId", 1234L)));
|
||||
|
||||
// when
|
||||
Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenChildDto.class).deserialize());
|
||||
@ -146,14 +201,14 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
public void shouldCreateIfRoleRequiredByReferencedEntityIsCoveredByUser() throws IOException {
|
||||
// given
|
||||
givenAuthenticatedUser();
|
||||
givenUserHavingRole(GivenDto.class, 1111L, Role.CONTRACTUAL_CONTACT);
|
||||
givenJSonTree(asJSon(ImmutablePair.of("parentId", 1111L)));
|
||||
givenUserHavingRole(GivenDto.class, 1234L, Role.CONTRACTUAL_CONTACT);
|
||||
givenJSonTree(asJSon(ImmutablePair.of("parentId", 1234L)));
|
||||
|
||||
// when
|
||||
final GivenChildDto actualDto = new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, null, GivenChildDto.class).deserialize();
|
||||
|
||||
// then
|
||||
assertThat(actualDto.parentId).isEqualTo(1111L);
|
||||
assertThat(actualDto.parentId).isEqualTo(1234L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -193,9 +248,12 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
given(codec.readTree(jsonParser)).willReturn(new ObjectMapper().readTree(givenJSon));
|
||||
}
|
||||
|
||||
public static class GivenDto {
|
||||
abstract class GivenService implements IdToDtoResolver<GivenDto> {
|
||||
}
|
||||
|
||||
@SelfId
|
||||
public static class GivenDto extends FluentBuilder<GivenDto> {
|
||||
|
||||
@SelfId(resolver = GivenService.class)
|
||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||
Long id;
|
||||
|
||||
@ -212,12 +270,12 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
Long openLongField;
|
||||
}
|
||||
|
||||
abstract class GivenService implements IdToDtoResolver<GivenDto> {
|
||||
abstract class GivenChildService implements IdToDtoResolver<GivenChildDto> {
|
||||
}
|
||||
|
||||
public static class GivenChildDto {
|
||||
public static class GivenChildDto extends FluentBuilder<GivenChildDto> {
|
||||
|
||||
@SelfId
|
||||
@SelfId(resolver = GivenChildService.class)
|
||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||
Long id;
|
||||
|
||||
@ -231,11 +289,11 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
||||
|
||||
public static class GivenDtoWithMultipleSelfId {
|
||||
|
||||
@SelfId
|
||||
@SelfId(resolver = GivenChildService.class)
|
||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||
Long id;
|
||||
|
||||
@SelfId
|
||||
@SelfId(resolver = GivenChildService.class)
|
||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||
Long id2;
|
||||
|
||||
|
@ -5,18 +5,22 @@ import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.hostsharing.hsadminng.service.IdToDtoResolver;
|
||||
import org.hostsharing.hsadminng.service.dto.FluentBuilder;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ -26,10 +30,16 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
public MockitoRule mockitoRule = MockitoJUnit.rule();
|
||||
|
||||
@Mock
|
||||
public ApplicationContext ctx;
|
||||
private ApplicationContext ctx;
|
||||
|
||||
@Mock
|
||||
public JsonGenerator jsonGenerator;
|
||||
private AutowireCapableBeanFactory autowireCapableBeanFactory;
|
||||
|
||||
@Mock
|
||||
private JsonGenerator jsonGenerator;
|
||||
|
||||
@Mock
|
||||
private GivenCustomerService givenCustomerService;
|
||||
|
||||
private final GivenDto givenDTO = createSampleDto();
|
||||
|
||||
@ -37,6 +47,12 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
public void init() {
|
||||
MockSecurityContext.givenAuthenticatedUser();
|
||||
MockSecurityContext.givenUserHavingRole(GivenCustomerDto.class, 888L, Role.ANY_CUSTOMER_USER);
|
||||
|
||||
given(ctx.getAutowireCapableBeanFactory()).willReturn(autowireCapableBeanFactory);
|
||||
given(autowireCapableBeanFactory.createBean(GivenCustomerService.class)).willReturn(givenCustomerService);
|
||||
given(givenCustomerService.findOne(888L)).willReturn(Optional.of(new GivenCustomerDto()
|
||||
.with(dto -> dto.id = 888L)
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -84,7 +100,7 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
}
|
||||
class GivenDtoWithUnimplementedFieldType {
|
||||
@AccessFor(read = Role.ANYBODY)
|
||||
Arbitrary fieldWithUnimplementedType;
|
||||
Arbitrary fieldWithUnimplementedType = new Arbitrary();
|
||||
}
|
||||
final GivenDtoWithUnimplementedFieldType givenDtoWithUnimplementedFieldType = new GivenDtoWithUnimplementedFieldType();
|
||||
|
||||
@ -107,8 +123,10 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
return dto;
|
||||
}
|
||||
|
||||
private static class GivenCustomerDto {
|
||||
|
||||
private static class GivenCustomerDto extends FluentBuilder<GivenCustomerDto> {
|
||||
@SelfId(resolver = GivenService.class)
|
||||
@AccessFor(read = Role.ANYBODY)
|
||||
Long id;
|
||||
}
|
||||
|
||||
private abstract class GivenCustomerService implements IdToDtoResolver<GivenCustomerDto> {
|
||||
@ -116,7 +134,12 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
|
||||
private static class GivenDto {
|
||||
|
||||
@SelfId(resolver = GivenService.class)
|
||||
@AccessFor(read = Role.ANYBODY)
|
||||
Long id;
|
||||
|
||||
@ParentId(resolver = GivenCustomerService.class)
|
||||
@AccessFor(read = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
Long customerId;
|
||||
|
||||
@AccessFor(read = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
@ -131,4 +154,9 @@ public class JSonSerializerWithAccessFilterUnitTest {
|
||||
@AccessFor(read = Role.ANYBODY)
|
||||
Long openLongField;
|
||||
}
|
||||
|
||||
private abstract class GivenService implements IdToDtoResolver<GivenService> {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -20,8 +20,13 @@ public class MockSecurityContext {
|
||||
|
||||
public static void givenUserHavingRole(final Class<?> onClass, final |