JSonDeserializerWithAccessFilter with working access rights validation
This commit is contained in:
parent
bb0fb4aa78
commit
63bd602397
@ -3,9 +3,9 @@ package org.hostsharing.hsadminng.service.accessfilter;
|
|||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@Documented
|
||||||
@Target({ElementType.FIELD, ElementType.TYPE_USE})
|
@Target({ElementType.FIELD, ElementType.TYPE_USE})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
|
||||||
public @interface AccessFor {
|
public @interface AccessFor {
|
||||||
Role[] init() default Role.NOBODY;
|
Role[] init() default Role.NOBODY;
|
||||||
|
|
||||||
|
@ -7,9 +7,13 @@ import com.fasterxml.jackson.databind.node.IntNode;
|
|||||||
import com.fasterxml.jackson.databind.node.LongNode;
|
import com.fasterxml.jackson.databind.node.LongNode;
|
||||||
import com.fasterxml.jackson.databind.node.TextNode;
|
import com.fasterxml.jackson.databind.node.TextNode;
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
|
import org.hostsharing.hsadminng.security.SecurityUtils;
|
||||||
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
||||||
|
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked;
|
import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked;
|
||||||
|
|
||||||
@ -17,6 +21,8 @@ public class JSonDeserializerWithAccessFilter<T> {
|
|||||||
|
|
||||||
private final T dto;
|
private final T dto;
|
||||||
private final TreeNode treeNode;
|
private final TreeNode treeNode;
|
||||||
|
private final Set<Field> modifiedFields = new HashSet<>();
|
||||||
|
private Field selfIdField = null;
|
||||||
|
|
||||||
public JSonDeserializerWithAccessFilter(final JsonParser jsonParser, final DeserializationContext deserializationContext, Class<T> dtoClass) {
|
public JSonDeserializerWithAccessFilter(final JsonParser jsonParser, final DeserializationContext deserializationContext, Class<T> dtoClass) {
|
||||||
this.treeNode = unchecked(() -> jsonParser.getCodec().readTree(jsonParser));
|
this.treeNode = unchecked(() -> jsonParser.getCodec().readTree(jsonParser));
|
||||||
@ -30,10 +36,13 @@ public class JSonDeserializerWithAccessFilter<T> {
|
|||||||
final Field field = dto.getClass().getDeclaredField(fieldName);
|
final Field field = dto.getClass().getDeclaredField(fieldName);
|
||||||
final Object value = readValue(treeNode, field);
|
final Object value = readValue(treeNode, field);
|
||||||
writeValue(dto, field, value);
|
writeValue(dto, field, value);
|
||||||
|
markAsModified(field);
|
||||||
} catch (NoSuchFieldException e) {
|
} catch (NoSuchFieldException e) {
|
||||||
throw new RuntimeException("setting field " + fieldName + " failed", e);
|
throw new RuntimeException("setting field " + fieldName + " failed", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modifiedFields.forEach(this::checkAccess);
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,4 +70,48 @@ public class JSonDeserializerWithAccessFilter<T> {
|
|||||||
throw new NotImplementedException("property type not yet implemented: " + field);
|
throw new NotImplementedException("property type not yet implemented: " + field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void markAsModified(final Field field) {
|
||||||
|
modifiedFields.add(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getId() {
|
||||||
|
if (selfIdField == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ReflectionUtil.getValue(dto, selfIdField);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAccess(final Field field) {
|
||||||
|
if ( !rememberSelfIdField(field) ) {
|
||||||
|
if (getId() == null) {
|
||||||
|
if (!getLoginUserRole().isAllowedToInit(field)) {
|
||||||
|
throw new BadRequestAlertException("Initialization of field prohibited for current user", toDisplay(field), "initializationProhibited");
|
||||||
|
}
|
||||||
|
} else if (getId() != null) {
|
||||||
|
if (!getLoginUserRole().isAllowedToUpdate(field)) {
|
||||||
|
throw new BadRequestAlertException("Update of field prohibited for current user", toDisplay(field), "updateProhibited");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean rememberSelfIdField(final Field field) {
|
||||||
|
if ( field.isAnnotationPresent(SelfId.class) ) {
|
||||||
|
if ( selfIdField != null ) {
|
||||||
|
throw new AssertionError("multiple " + SelfId.class + " detected in " + field.getDeclaringClass().getSimpleName() );
|
||||||
|
}
|
||||||
|
selfIdField = field;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toDisplay(final Field field) {
|
||||||
|
return field.getDeclaringClass().getSimpleName() + "." + field.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Role getLoginUserRole() {
|
||||||
|
return SecurityUtils.getCurrentUserLogin().map(u -> Role.valueOf(u.toUpperCase())).orElse(Role.ANYBODY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package org.hostsharing.hsadminng.service.accessfilter;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to mark a field within a DTO as containing the id of a field,
|
||||||
|
* it's needed to identify an existing entity for update functions.
|
||||||
|
* Initialization and update rights have no meaning for such fields,
|
||||||
|
* its initialized automatically and never updated.
|
||||||
|
*
|
||||||
|
* @see AccessFor
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface SelfId {
|
||||||
|
}
|
@ -2,17 +2,11 @@ package org.hostsharing.hsadminng.service.dto;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
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.JsonSerializer;
|
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
import com.fasterxml.jackson.databind.node.IntNode;
|
import org.hostsharing.hsadminng.service.accessfilter.*;
|
||||||
import com.fasterxml.jackson.databind.node.TextNode;
|
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.AccessFor;
|
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.JSonDeserializerWithAccessFilter;
|
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.JSonSerializerWithAccessFilter;
|
|
||||||
import org.hostsharing.hsadminng.service.accessfilter.Role;
|
|
||||||
import org.springframework.boot.jackson.JsonComponent;
|
import org.springframework.boot.jackson.JsonComponent;
|
||||||
|
|
||||||
import javax.validation.constraints.*;
|
import javax.validation.constraints.*;
|
||||||
@ -25,6 +19,7 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public class CustomerDTO implements Serializable {
|
public class CustomerDTO implements Serializable {
|
||||||
|
|
||||||
|
@SelfId
|
||||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@ -26,6 +26,15 @@ public class ReflectionUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> Object getValue(T dto, Field field) {
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(dto);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ThrowingSupplier<T> {
|
public interface ThrowingSupplier<T> {
|
||||||
T get() throws Exception;
|
T get() throws Exception;
|
||||||
|
@ -11,32 +11,32 @@ public class BadRequestAlertException extends AbstractThrowableProblem {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final String entityName;
|
private final String param;
|
||||||
|
|
||||||
private final String errorKey;
|
private final String errorKey;
|
||||||
|
|
||||||
public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) {
|
public BadRequestAlertException(String defaultMessage, String param, String errorKey) {
|
||||||
this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey);
|
this(ErrorConstants.DEFAULT_TYPE, defaultMessage, param, errorKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) {
|
public BadRequestAlertException(URI type, String defaultMessage, String param, String errorKey) {
|
||||||
super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey));
|
super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(param, errorKey));
|
||||||
this.entityName = entityName;
|
this.param = param;
|
||||||
this.errorKey = errorKey;
|
this.errorKey = errorKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEntityName() {
|
public String getParam() {
|
||||||
return entityName;
|
return param;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getErrorKey() {
|
public String getErrorKey() {
|
||||||
return errorKey;
|
return errorKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, Object> getAlertParameters(String entityName, String errorKey) {
|
private static Map<String, Object> getAlertParameters(String param, String errorKey) {
|
||||||
Map<String, Object> parameters = new HashMap<>();
|
Map<String, Object> parameters = new HashMap<>();
|
||||||
parameters.put("message", "error." + errorKey);
|
parameters.put("message", "error." + errorKey);
|
||||||
parameters.put("params", entityName);
|
parameters.put("params", param);
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ public class ExceptionTranslator implements ProblemHandling {
|
|||||||
|
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
public ResponseEntity<Problem> handleBadRequestAlertException(BadRequestAlertException ex, NativeWebRequest request) {
|
public ResponseEntity<Problem> handleBadRequestAlertException(BadRequestAlertException ex, NativeWebRequest request) {
|
||||||
return create(ex, request, HeaderUtil.createFailureAlert(ex.getEntityName(), ex.getErrorKey(), ex.getMessage()));
|
return create(ex, request, HeaderUtil.createFailureAlert(ex.getParam(), ex.getErrorKey(), ex.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
"shareTransactionImmutable": "Transaktionen mit Geschäftsanteilen sind unveränderlich",
|
"shareTransactionImmutable": "Transaktionen mit Geschäftsanteilen sind unveränderlich",
|
||||||
"membershipNotDeletable": "Mitgliedschaft kann nicht gelöscht werden, setze stattdessen das 'untilDate'",
|
"membershipNotDeletable": "Mitgliedschaft kann nicht gelöscht werden, setze stattdessen das 'untilDate'",
|
||||||
"untilDateMustBeAfterSinceDate": "Mitgliedshafts-Austrittsdatum muss nach dem Beitrittsdatum liegen",
|
"untilDateMustBeAfterSinceDate": "Mitgliedshafts-Austrittsdatum muss nach dem Beitrittsdatum liegen",
|
||||||
"anotherUncancelledMembershipExists": "Nur eine einzige ungekündigte Mitgliedschaft pro Kunde ist zulässig"
|
"anotherUncancelledMembershipExists": "Nur eine einzige ungekündigte Mitgliedschaft pro Kunde ist zulässig",
|
||||||
|
"initializationProhibited": "Initialisierung des Feldes unzulässig",
|
||||||
|
"updateProhibited": "Aktualisierung des Feldes unzulässig"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
"shareTransactionImmutable": "Share transactions are immutable",
|
"shareTransactionImmutable": "Share transactions are immutable",
|
||||||
"membershipNotDeletable": "Membership cannot be deleted, instead set 'untilDate'",
|
"membershipNotDeletable": "Membership cannot be deleted, instead set 'untilDate'",
|
||||||
"untilDateMustBeAfterSinceDate": "Membership until date must be after since date",
|
"untilDateMustBeAfterSinceDate": "Membership until date must be after since date",
|
||||||
"anotherUncancelledMembershipExists": "Only a single uncancelled membership allowed per customer"
|
"anotherUncancelledMembershipExists": "Only a single uncancelled membership allowed per customer",
|
||||||
|
"initializationProhibited": "Initialization of the field prohibited",
|
||||||
|
"updateProhibited": "Update of the field prohibited"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
package org.hostsharing.hsadminng.service.accessfilter;
|
package org.hostsharing.hsadminng.service.accessfilter;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.annotation.JsonTypeId;
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
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.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
|
||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
import org.hostsharing.hsadminng.service.dto.CustomerDTO;
|
import org.hostsharing.hsadminng.web.rest.errors.BadRequestAlertException;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -22,9 +20,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
import static org.assertj.core.api.Assertions.catchThrowable;
|
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||||
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenLoginUserWithRole;
|
import static org.hostsharing.hsadminng.service.accessfilter.MockSecurityContext.givenLoginUserWithRole;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
public class JSonDeserializerWithAccessFilterUnitTest {
|
public class JSonDeserializerWithAccessFilterUnitTest {
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@ -40,7 +37,7 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
|||||||
public TreeNode treeNode;
|
public TreeNode treeNode;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() throws IOException {
|
public void init() {
|
||||||
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||||
|
|
||||||
given(jsonParser.getCodec()).willReturn(codec);
|
given(jsonParser.getCodec()).willReturn(codec);
|
||||||
@ -82,11 +79,71 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
|||||||
assertThat(actualDto.openLongField).isEqualTo(1234L);
|
assertThat(actualDto.openLongField).isEqualTo(1234L);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- fixture code below ---
|
@Test
|
||||||
|
public void shouldDeserializeStringFieldIfRequiredRoleIsCoveredByUser() throws IOException {
|
||||||
|
// given
|
||||||
|
givenLoginUserWithRole(Role.FINANCIAL_CONTACT);
|
||||||
|
givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||||
|
|
||||||
private String asJSon(final ImmutablePair<String, ? extends Object>... properties) {
|
// when
|
||||||
|
GivenDto actualDto = new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenDto.class).deserialize();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(actualDto.restrictedField).isEqualTo("Restricted String Value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldInitializeFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
|
||||||
|
// given
|
||||||
|
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||||
|
givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||||
|
|
||||||
|
// when
|
||||||
|
Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(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 shouldUpdateFieldIfRequiredRoleIsNotCoveredByUser() throws IOException {
|
||||||
|
// given
|
||||||
|
givenLoginUserWithRole(Role.ANY_CUSTOMER_USER);
|
||||||
|
givenJSonTree(asJSon(
|
||||||
|
ImmutablePair.of("id", 1234L),
|
||||||
|
ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||||
|
|
||||||
|
// when
|
||||||
|
Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenDto.class).deserialize());
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(exception).isInstanceOfSatisfying(BadRequestAlertException.class, badRequestAlertException -> {
|
||||||
|
assertThat(badRequestAlertException.getParam()).isEqualTo("GivenDto.restrictedField");
|
||||||
|
assertThat(badRequestAlertException.getErrorKey()).isEqualTo("updateProhibited");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should() throws IOException {
|
||||||
|
// given
|
||||||
|
givenJSonTree(asJSon(ImmutablePair.of("restrictedField", "Restricted String Value")));
|
||||||
|
|
||||||
|
// when
|
||||||
|
Throwable exception = catchThrowable(() -> new JSonDeserializerWithAccessFilter<>(jsonParser, null, GivenDtoWithMultipleSelfId.class).deserialize());
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(exception).isInstanceOf(AssertionError.class).hasMessageContaining("xx");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- only fixture code below ---
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
private final String asJSon(final ImmutablePair<String, Object>... properties) {
|
||||||
final StringBuilder json = new StringBuilder();
|
final StringBuilder json = new StringBuilder();
|
||||||
for ( ImmutablePair<String, ? extends Object> prop: properties ) {
|
for (ImmutablePair<String, Object> prop : properties) {
|
||||||
json.append(inQuotes(prop.left));
|
json.append(inQuotes(prop.left));
|
||||||
json.append(": ");
|
json.append(": ");
|
||||||
if (prop.right instanceof Number) {
|
if (prop.right instanceof Number) {
|
||||||
@ -108,16 +165,34 @@ public class JSonDeserializerWithAccessFilterUnitTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class GivenDto {
|
public static class GivenDto {
|
||||||
@AccessFor(update = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
|
|
||||||
|
@SelfId
|
||||||
|
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@AccessFor(init = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT}, update = {Role.TECHNICAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||||
String restrictedField;
|
String restrictedField;
|
||||||
|
|
||||||
@AccessFor(update = Role.ANYBODY)
|
@AccessFor(init = Role.ANYBODY, update = Role.ANYBODY)
|
||||||
String openStringField;
|
String openStringField;
|
||||||
|
|
||||||
@AccessFor(update = Role.ANYBODY)
|
@AccessFor(init = Role.ANYBODY, update = Role.ANYBODY)
|
||||||
Integer openIntegerField;
|
Integer openIntegerField;
|
||||||
|
|
||||||
@AccessFor(update = Role.ANYBODY)
|
@AccessFor(init = Role.ANYBODY, update = Role.ANYBODY)
|
||||||
Long openLongField;
|
Long openLongField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class GivenDtoWithMultipleSelfId {
|
||||||
|
|
||||||
|
@SelfId
|
||||||
|
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@SelfId
|
||||||
|
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||||
|
Long id2;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user