refactoring EntityController

This commit is contained in:
anhefti 2019-04-11 14:26:05 +02:00
parent 2b3d4aa27d
commit 201e37914e
6 changed files with 107 additions and 46 deletions

View file

@ -104,7 +104,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
.andForRole(UserRole.INSTITUTIONAL_ADMIN) .andForRole(UserRole.INSTITUTIONAL_ADMIN)
.withInstitutionalPrivilege(PrivilegeType.READ) .withInstitutionalPrivilege(PrivilegeType.READ)
.andForRole(UserRole.EXAM_ADMIN) .andForRole(UserRole.EXAM_ADMIN)
.withInstitutionalPrivilege(PrivilegeType.MODIFY) .withInstitutionalPrivilege(PrivilegeType.WRITE)
.withOwnerPrivilege(PrivilegeType.WRITE) .withOwnerPrivilege(PrivilegeType.WRITE)
.andForRole(UserRole.EXAM_SUPPORTER) .andForRole(UserRole.EXAM_SUPPORTER)
.withInstitutionalPrivilege(PrivilegeType.READ) .withInstitutionalPrivilege(PrivilegeType.READ)
@ -117,7 +117,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
.andForRole(UserRole.INSTITUTIONAL_ADMIN) .andForRole(UserRole.INSTITUTIONAL_ADMIN)
.withInstitutionalPrivilege(PrivilegeType.READ) .withInstitutionalPrivilege(PrivilegeType.READ)
.andForRole(UserRole.EXAM_ADMIN) .andForRole(UserRole.EXAM_ADMIN)
.withInstitutionalPrivilege(PrivilegeType.MODIFY) .withInstitutionalPrivilege(PrivilegeType.WRITE)
.withOwnerPrivilege(PrivilegeType.WRITE) .withOwnerPrivilege(PrivilegeType.WRITE)
.andForRole(UserRole.EXAM_SUPPORTER) .andForRole(UserRole.EXAM_SUPPORTER)
.withInstitutionalPrivilege(PrivilegeType.READ) .withInstitutionalPrivilege(PrivilegeType.READ)

View file

@ -11,12 +11,49 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
/** Service to address bulk actions like activation or deletion where the action
* or state-change of one Entity has an effect on other entities that that has
* a relation to the source entity.
* <p>
* A bulk action for a specified entity instance will be first applied to all its dependent
* child-entities. For example if one is going to delete/deactivate a particular LMS Setup, all
* Exams imported from this LMSSetup are first deactivated and all Exam Config Mapping and
* all Client Connection are of all the Exams are deactivated first.
* <p>
* below is the relation-tree of known node-entities of the SEB Server application
* <code>
* Institution
* ____________ / | \______________
* / | \
* LMS Setup | User-Account
* | |
* Exam Configuration
* |\ /
* | Exam Config Mapping
* |
* Client Connection
* </code> */
public interface BulkActionService { public interface BulkActionService {
/** Use this to collect all EntityKey's of all dependent entities for a given BulkAction.
*
* @param action the BulkAction defining the source entity keys and acts also as the
* dependency collector */
void collectDependencies(BulkAction action); void collectDependencies(BulkAction action);
/** This executes a given BulkAction by first getting all dependencies and applying
* the action to that first and then applying the action to the source entities of
* the BulkAction.
*
* @param action the BulkAction that defines at least the type and the source entity keys
* @return The BulkAction containing the result of the execution */
Result<BulkAction> doBulkAction(BulkAction action); Result<BulkAction> doBulkAction(BulkAction action);
/** Creates a EntityProcessingReport from a given BulkAction result.
* If the given BulkAction has not already been executed, it will be executed first
*
* @param action the BulkAction of a concrete type
* @return EntityProcessingReport extracted form an executed BulkAxtion */
Result<EntityProcessingReport> createReport(BulkAction action); Result<EntityProcessingReport> createReport(BulkAction action);
} }

View file

@ -336,14 +336,17 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
@Override @Override
@Transactional @Transactional
public Result<UserActivityLog> save(final UserActivityLog modified) { public Result<UserActivityLog> save(final UserActivityLog modified) {
// TODO Auto-generated method stub throw new UnsupportedOperationException();
return Result.ofTODO();
} }
@Override @Override
public Result<UserActivityLog> createNew(final UserActivityLog data) { public Result<UserActivityLog> createNew(final UserActivityLog data) {
// TODO Auto-generated method stub return log(
return Result.ofTODO(); data.activityType,
data.entityType,
data.entityId,
data.message,
data);
} }
@Override @Override

View file

@ -40,7 +40,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
* @param <T> The concrete Entity domain-model type used on all GET, PUT * @param <T> The concrete Entity domain-model type used on all GET, PUT
* @param <M> The concrete Entity domain-model type used for POST methods (new) */ * @param <M> The concrete Entity domain-model type used for POST methods (new) */
public abstract class ActivatableEntityController<T extends GrantEntity, M extends GrantEntity> public abstract class ActivatableEntityController<T extends GrantEntity, M extends GrantEntity>
extends GrantEntityController<T, M> { extends EntityController<T, M> {
public ActivatableEntityController( public ActivatableEntityController(
final AuthorizationService authorizationGrantService, final AuthorizationService authorizationGrantService,

View file

@ -55,7 +55,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
* *
* @param <T> The concrete Entity domain-model type used on all GET, PUT * @param <T> The concrete Entity domain-model type used on all GET, PUT
* @param <M> The concrete Entity domain-model type used for POST methods (new) */ * @param <M> The concrete Entity domain-model type used for POST methods (new) */
public abstract class GrantEntityController<T extends GrantEntity, M extends GrantEntity> { public abstract class EntityController<T extends Entity, M extends Entity> {
protected final AuthorizationService authorization; protected final AuthorizationService authorization;
protected final BulkActionService bulkActionService; protected final BulkActionService bulkActionService;
@ -64,7 +64,7 @@ public abstract class GrantEntityController<T extends GrantEntity, M extends Gra
protected final PaginationService paginationService; protected final PaginationService paginationService;
protected final BeanValidationService beanValidationService; protected final BeanValidationService beanValidationService;
protected GrantEntityController( protected EntityController(
final AuthorizationService authorization, final AuthorizationService authorization,
final BulkActionService bulkActionService, final BulkActionService bulkActionService,
final EntityDAO<T, M> entityDAO, final EntityDAO<T, M> entityDAO,
@ -196,7 +196,7 @@ public abstract class GrantEntityController<T extends GrantEntity, M extends Gra
this.entityDAO this.entityDAO
.byModelId(modelId) .byModelId(modelId)
.flatMap(this.authorization::checkRead); .map(this::checkReadAccess);
final BulkAction bulkAction = new BulkAction( final BulkAction bulkAction = new BulkAction(
bulkActionType, bulkActionType,
@ -220,7 +220,7 @@ public abstract class GrantEntityController<T extends GrantEntity, M extends Gra
return this.entityDAO return this.entityDAO
.byModelId(modelId) .byModelId(modelId)
.flatMap(this.authorization::checkRead) .flatMap(this::checkReadAccess)
.getOrThrow(); .getOrThrow();
} }
@ -246,7 +246,7 @@ public abstract class GrantEntityController<T extends GrantEntity, M extends Gra
.flatMap(this.entityDAO::byEntityKeys) .flatMap(this.entityDAO::byEntityKeys)
.getOrThrow() .getOrThrow()
.stream() .stream()
.filter(this.authorization::hasReadonlyGrant) .filter(this::hasReadAccess)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -276,7 +276,7 @@ public abstract class GrantEntityController<T extends GrantEntity, M extends Gra
final M requestModel = this.createNew(postMap); final M requestModel = this.createNew(postMap);
return this.authorization.checkWrite(requestModel) return this.checkCreateAccess(requestModel)
.flatMap(this::validForCreate) .flatMap(this::validForCreate)
.flatMap(this.entityDAO::createNew) .flatMap(this.entityDAO::createNew)
.flatMap(this.userActivityLogDAO::logCreate) .flatMap(this.userActivityLogDAO::logCreate)
@ -294,7 +294,7 @@ public abstract class GrantEntityController<T extends GrantEntity, M extends Gra
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public T savePut(@Valid @RequestBody final T modifyData) { public T savePut(@Valid @RequestBody final T modifyData) {
return this.authorization.checkModify(modifyData) return this.checkModifyAccess(modifyData)
.flatMap(this::validForSave) .flatMap(this::validForSave)
.flatMap(this.entityDAO::save) .flatMap(this.entityDAO::save)
.flatMap(this.userActivityLogDAO::logModify) .flatMap(this.userActivityLogDAO::logModify)
@ -302,35 +302,6 @@ public abstract class GrantEntityController<T extends GrantEntity, M extends Gra
.getOrThrow(); .getOrThrow();
} }
// ******************
// * PATCH (save)
// ******************
// NOTE: not supported yet because of difficulties on conversion of params-map to json object
// @RequestMapping(
// path = "/{id}",
// method = RequestMethod.PATCH,
// consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
// produces = MediaType.APPLICATION_JSON_VALUE)
// public T savePost(
// @PathVariable final String id,
// @RequestParam final MultiValueMap<String, String> allRequestParams,
// @RequestParam(
// name = Entity.FILTER_ATTR_INSTITUTION,
// required = true,
// defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) {
//
// allRequestParams.putIfAbsent(
// Domain.ATTR_INSTITUTION_ID,
// Arrays.asList(String.valueOf(institutionId)));
// final M requestModel = this.toRequestModel(null, allRequestParams);
//
// return this.entityDAO.save(id, requestModel)
// .flatMap(entity -> this.userActivityLogDAO.log(ActivityType.MODIFY, entity))
// .flatMap(entity -> notifySaved(requestModel, entity))
// .getOrThrow();
// }
// ****************** // ******************
// * DELETE (delete) // * DELETE (delete)
// ****************** // ******************
@ -347,7 +318,7 @@ public abstract class GrantEntityController<T extends GrantEntity, M extends Gra
new EntityKey(modelId, entityType)); new EntityKey(modelId, entityType));
return this.entityDAO.byModelId(modelId) return this.entityDAO.byModelId(modelId)
.flatMap(this.authorization::checkWrite) .flatMap(this::checkWriteAccess)
.flatMap(entity -> this.bulkActionService.createReport(bulkAction)) .flatMap(entity -> this.bulkActionService.createReport(bulkAction))
.getOrThrow(); .getOrThrow();
} }
@ -362,7 +333,7 @@ public abstract class GrantEntityController<T extends GrantEntity, M extends Gra
protected Result<Collection<T>> getAll(final FilterMap filterMap) { protected Result<Collection<T>> getAll(final FilterMap filterMap) {
return this.entityDAO.allMatching( return this.entityDAO.allMatching(
filterMap, filterMap,
this.authorization::hasReadonlyGrant); this::hasReadAccess);
} }
protected Result<T> notifyCreated(final T entity) { protected Result<T> notifyCreated(final T entity) {
@ -390,6 +361,56 @@ public abstract class GrantEntityController<T extends GrantEntity, M extends Gra
return Result.of(entity); return Result.of(entity);
} }
private Result<T> checkReadAccess(final T entity) {
final GrantEntity grantEntity = toGrantEntity(entity);
if (grantEntity != null) {
this.authorization.checkRead(grantEntity);
}
return Result.of(entity);
}
private boolean hasReadAccess(final T entity) {
final GrantEntity grantEntity = toGrantEntity(entity);
if (grantEntity != null) {
return this.authorization.hasReadonlyGrant(grantEntity);
}
return true;
}
private Result<T> checkModifyAccess(final T entity) {
final GrantEntity grantEntity = toGrantEntity(entity);
if (grantEntity != null) {
this.authorization.checkModify(grantEntity);
}
return Result.of(entity);
}
private Result<T> checkWriteAccess(final T entity) {
final GrantEntity grantEntity = toGrantEntity(entity);
if (grantEntity != null) {
this.authorization.checkWrite(grantEntity);
}
return Result.of(entity);
}
private Result<M> checkCreateAccess(final M entity) {
final GrantEntity grantEntity = toGrantEntity(entity);
if (grantEntity != null) {
this.authorization.checkWrite(grantEntity);
}
return Result.of(entity);
}
protected GrantEntity toGrantEntity(final Entity entity) {
if (entity instanceof GrantEntity) {
return (GrantEntity) entity;
}
throw new IllegalArgumentException("Entity instance is not of type GrantEntity. "
+ "Do override the toGrantEntity method from EntityController within the specific -Controller implementation");
}
protected abstract M createNew(POSTMapper postParams); protected abstract M createNew(POSTMapper postParams);
protected abstract SqlTable getSQLTableOfEntity(); protected abstract SqlTable getSQLTableOfEntity();

View file

@ -29,7 +29,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
@WebServiceProfile @WebServiceProfile
@RestController @RestController
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.EXAM_INDICATOR_ENDPOINT) @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.EXAM_INDICATOR_ENDPOINT)
public class IndicatorController extends GrantEntityController<Indicator, Indicator> { public class IndicatorController extends EntityController<Indicator, Indicator> {
private final ExamDAO examDao; private final ExamDAO examDao;