split up AssetDTOUnitTest and AssetDTOIntTest, AccessMappingsUnitTestBase
This commit is contained in:
@ -130,9 +130,10 @@ public class JSonDeserializationWithAccessFilter<T> extends JSonAccessFilter<T>
|
||||
|
||||
private void checkAccessToWrittenFields(final T currentDto) {
|
||||
writtenFields.forEach(field -> {
|
||||
// TODO this ugly code needs cleanup
|
||||
if (!field.equals(selfIdField)) {
|
||||
final Role role = getLoginUserRole();
|
||||
if (getId() == null) {
|
||||
if (isInitAccess()) {
|
||||
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");
|
||||
@ -140,14 +141,18 @@ public class JSonDeserializationWithAccessFilter<T> extends JSonAccessFilter<T>
|
||||
throw new BadRequestAlertException("Referencing field " + toDisplay(field) + " prohibited for current user role " + role, toDisplay(field), "referencingProhibited");
|
||||
}
|
||||
}
|
||||
} else if (isUpdate(field, dto, currentDto) && !getLoginUserRole().isAllowedToUpdate(field)) {
|
||||
} else if ( !Role.toBeIgnoredForUpdates(field) && isActuallyUpdated(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) {
|
||||
private boolean isInitAccess() {
|
||||
return getId() == null;
|
||||
}
|
||||
|
||||
private boolean isActuallyUpdated(final Field field, final T dto, T currentDto) {
|
||||
return ObjectUtils.notEqual(ReflectionUtil.getValue(dto, field), ReflectionUtil.getValue(currentDto, field));
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,16 @@ package org.hostsharing.hsadminng.service.accessfilter;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
|
||||
/**
|
||||
* These enum values are on the one hand used to define the minimum role required to grant access to resources,
|
||||
* but on the other hand also for the roles users can be assigned to.
|
||||
*
|
||||
* <p>
|
||||
* TODO: Maybe splitting it up into UserRole and RequiredRole would make it more clear?
|
||||
* And maybe instead of a level, we could then add the comprised roles in the constructor?
|
||||
* This could also be a better way to express that the financial contact has no rights to
|
||||
* other users resources (see also ACTUAL_CUSTOMER_USEr vs. ANY_CUSTOMER_USER).
|
||||
* And maybe instead of a level, we could then add the comprised roles in the constructor?
|
||||
* This could also be a better way to express that the financial contact has no rights to
|
||||
* other users resources (see also ACTUAL_CUSTOMER_USEr vs. ANY_CUSTOMER_USER).
|
||||
*/
|
||||
public enum Role {
|
||||
/**
|
||||
@ -79,14 +81,45 @@ public enum Role {
|
||||
* This role is meant to specify that a resources can be accessed by anybody, even without login.
|
||||
* It's currently only used for technical purposes.
|
||||
*/
|
||||
ANYBODY(99);
|
||||
ANYBODY(99),
|
||||
|
||||
private final int level;
|
||||
/**
|
||||
* Pseudo-role to mark init/update access as ignored because the field is display-only.
|
||||
* This allows REST clients to send the whole response back as a new update request.
|
||||
* This role is not covered by any and covers itself no role.
|
||||
*/
|
||||
IGNORED;
|
||||
|
||||
private final Integer level;
|
||||
|
||||
Role() {
|
||||
this.level = null;
|
||||
}
|
||||
|
||||
Role(final int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param field a field of a DTO with AccessMappings
|
||||
* @return true if update access can be ignored because the field is just for display anyway
|
||||
*/
|
||||
public static boolean toBeIgnoredForUpdates(final Field field) {
|
||||
final AccessFor accessForAnnot = field.getAnnotation(AccessFor.class);
|
||||
if (accessForAnnot == null) {
|
||||
return true;
|
||||
}
|
||||
final Role[] updateAccessFor = field.getAnnotation(AccessFor.class).update();
|
||||
return updateAccessFor.length == 1 && updateAccessFor[0].isIgnored();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the role is the IGNORED role
|
||||
*/
|
||||
public boolean isIgnored() {
|
||||
return this == Role.IGNORED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this role is independent of a target object, false otherwise.
|
||||
*/
|
||||
@ -95,12 +128,12 @@ public enum Role {
|
||||
}
|
||||
|
||||
/**
|
||||
@return the role with the broadest access rights
|
||||
* @return the role with the broadest access rights
|
||||
*/
|
||||
public static Role broadest(final Role role, final Role... roles) {
|
||||
Role broadests = role;
|
||||
for ( Role r: roles ) {
|
||||
if ( r.covers(broadests)) {
|
||||
for (Role r : roles) {
|
||||
if (r.covers(broadests)) {
|
||||
broadests = r;
|
||||
}
|
||||
}
|
||||
@ -108,27 +141,51 @@ public enum Role {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given role is covered by this role.
|
||||
*
|
||||
* Determines if 'this' actual role covered the given required role.
|
||||
* <p>
|
||||
* Where 'this' means the Java instance itself as a role of a system user.
|
||||
*
|
||||
* <p>
|
||||
* {@code
|
||||
* Role.HOSTMASTER.covers(Role.ANY_CUSTOMER_USER) == true
|
||||
* }
|
||||
*
|
||||
* @param role The required role for a resource.
|
||||
*
|
||||
* @return whether this role comprises the given role
|
||||
*/
|
||||
public boolean covers(final Role role) {
|
||||
if (this.isIgnored() || role.isIgnored()) {
|
||||
return false;
|
||||
}
|
||||
return this == role || this.level < role.level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if 'this' actual role covers any of the given required roles.
|
||||
* <p>
|
||||
* Where 'this' means the Java instance itself as a role of a system user.
|
||||
* <p>
|
||||
* {@code
|
||||
* Role.HOSTMASTER.coversAny(Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT) == true
|
||||
* }
|
||||
*
|
||||
* @param roles The alternatively required roles for a resource. Must be at least one.
|
||||
* @return whether this role comprises any of the given roles
|
||||
*/
|
||||
public boolean coversAny(final Role... roles) {
|
||||
verify(roles != null && roles.length > 0, "roles expected");
|
||||
|
||||
for (Role role : roles) {
|
||||
if (this.covers(role)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this role of a user allows to initialize the given field when creating the resource.
|
||||
*
|
||||
* @param field a field of the DTO of a resource
|
||||
*
|
||||
* @return true if allowed
|
||||
*/
|
||||
public boolean isAllowedToInit(final Field field) {
|
||||
@ -145,7 +202,6 @@ public enum Role {
|
||||
* Checks if this role of a user allows to update the given field.
|
||||
*
|
||||
* @param field a field of the DTO of a resource
|
||||
*
|
||||
* @return true if allowed
|
||||
*/
|
||||
public boolean isAllowedToUpdate(final Field field) {
|
||||
@ -162,7 +218,6 @@ public enum Role {
|
||||
* Checks if this role of a user allows to read the given field.
|
||||
*
|
||||
* @param field a field of the DTO of a resource
|
||||
*
|
||||
* @return true if allowed
|
||||
*/
|
||||
public boolean isAllowedToRead(final Field field) {
|
||||
|
@ -20,23 +20,23 @@ import java.util.Objects;
|
||||
public class AssetDTO implements Serializable, AccessMappings {
|
||||
|
||||
@SelfId(resolver = AssetService.class)
|
||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||
@AccessFor(read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private Long id;
|
||||
|
||||
@NotNull
|
||||
@AccessFor(init = Role.ADMIN, update = 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, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private LocalDate valueDate;
|
||||
|
||||
@NotNull
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private AssetAction action;
|
||||
|
||||
@NotNull
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private BigDecimal amount;
|
||||
|
||||
@Size(max = 160)
|
||||
@ -44,12 +44,10 @@ public class AssetDTO implements Serializable, AccessMappings {
|
||||
private String remark;
|
||||
|
||||
@ParentId(resolver = MembershipService.class)
|
||||
@AccessFor(init = Role.ADMIN, update = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
@AccessFor(init = Role.ADMIN, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private Long membershipId;
|
||||
|
||||
// TODO: these init/update rights actually mean "ignore", we might want to express this in a better way
|
||||
// background: there is no converter for any display label in DTOs to entity field values anyway
|
||||
@AccessFor(init=Role.ANYBODY, update = Role.ANYBODY, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
@AccessFor(update = Role.IGNORED, read = {Role.CONTRACTUAL_CONTACT, Role.FINANCIAL_CONTACT})
|
||||
private String membershipDisplayLabel;
|
||||
|
||||
public Long getId() {
|
||||
@ -147,7 +145,7 @@ public class AssetDTO implements Serializable, AccessMappings {
|
||||
", amount=" + getAmount() +
|
||||
", remark='" + getRemark() + "'" +
|
||||
", membership=" + getMembershipId() +
|
||||
", membership='" + getMembershipDisplayLabel() + "'" +
|
||||
", membershipDisplayLabel='" + getMembershipDisplayLabel() + "'" +
|
||||
"}";
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user