diff --git a/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/controller/AbstractGsrsEntityController.java b/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/controller/AbstractGsrsEntityController.java index b71df4c3..50545037 100644 --- a/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/controller/AbstractGsrsEntityController.java +++ b/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/controller/AbstractGsrsEntityController.java @@ -155,6 +155,11 @@ public ResponseEntity createEntity(@RequestBody JsonNode newEntityJson, @RequestParam Map queryParameters, Principal principal) throws IOException { GsrsEntityService entityService = getEntityService(); + if( entityService.isReadOnly()) { + log.warn("detected forbidden operation in createEntity"); + String message = "Please use the parent object to perform this operation"; + return new ResponseEntity<>(message, gsrsControllerConfiguration.getHttpStatusFor(HttpStatus.BAD_REQUEST, queryParameters)); + } AbstractGsrsEntityService.CreationResult result =null; try { result = entityService.createEntity(newEntityJson); @@ -202,7 +207,13 @@ public ValidationResponse validateEntity(@RequestBody JsonNode updatedEntityJ public ResponseEntity updateEntity(@RequestBody JsonNode updatedEntityJson, @RequestParam Map queryParameters, Principal principal) throws Exception { - AbstractGsrsEntityService.UpdateResult result = getEntityService().updateEntity(updatedEntityJson); + if( getEntityService().isReadOnly()) { + log.warn("detected forbidden operation in createEntity"); + String message = "Please use the parent object to perform this operation"; + return new ResponseEntity<>(message, gsrsControllerConfiguration.getHttpStatusFor(HttpStatus.BAD_REQUEST, queryParameters)); + } + + AbstractGsrsEntityService.UpdateResult result = getEntityService().updateEntity(updatedEntityJson); if(result.getStatus()== AbstractGsrsEntityService.UpdateResult.STATUS.NOT_FOUND){ return gsrsControllerConfiguration.handleNotFound(queryParameters); } @@ -479,7 +490,13 @@ public ResponseEntity getById(@PathVariable("id") String id, @RequestPar @PreAuthorize("isAuthenticated()") @DeleteGsrsRestApiMapping(value = {"({id})", "/{id}"}) public ResponseEntity deleteById(@PathVariable("id") String id, @RequestParam Map queryParameters){ + if( getEntityService().isReadOnly()) { + log.warn("detected forbidden operation in createEntity"); + String message = "Please use the parent object to perform this operation"; + return new ResponseEntity<>(message, gsrsControllerConfiguration.getHttpStatusFor(HttpStatus.BAD_REQUEST, queryParameters)); + } Optional idOptional = getEntityService().getEntityIdOnlyBySomeIdentifier(id); + if(idOptional.isPresent()){ getEntityService().delete(idOptional.get()); return new ResponseEntity<>(HttpStatus.NO_CONTENT); diff --git a/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/service/AbstractGsrsEntityService.java b/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/service/AbstractGsrsEntityService.java index 60148252..ba9e51d5 100644 --- a/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/service/AbstractGsrsEntityService.java +++ b/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/service/AbstractGsrsEntityService.java @@ -84,6 +84,8 @@ public abstract class AbstractGsrsEntityService implements GsrsEntityServic private final String context; private final Pattern idPattern; + private boolean readOnly = false; + private CachedSupplier validatorFactory; /** * Create a new GSRS Entity Service with the given context. @@ -290,7 +292,10 @@ public EntityManager getEntityManager() { @Override public CreationResult createEntity(JsonNode newEntityJson, boolean partOfBatchLoad){ - + if( isReadOnly()){ + log.error("Trying to create a {} when service is read-only", getFriendlyName()); + throw new RuntimeException("Please use the parent object to create a " + getFriendlyName()); + } TransactionTemplate transactionTemplate = new TransactionTemplate(this.getTransactionManager()); ValidatorConfig.METHOD_TYPE methodType = partOfBatchLoad? ValidatorConfig.METHOD_TYPE.BATCH : ValidatorConfig.METHOD_TYPE.CREATE; @@ -416,6 +421,11 @@ public PlatformTransactionManager getTransactionManager() { @Override public UpdateResult updateEntity(T updatedEntity, EntityPersistAdapter.ChangeOperation changeOperation) throws Exception { + log.trace("updateEntity 2 parms"); + if( isReadOnly()){ + log.error("Trying to update a {} when service is read-only", getFriendlyName()); + throw new RuntimeException("Please use the parent object to update a " + getFriendlyName()); + } TransactionTemplate transactionTemplate = new TransactionTemplate(getTransactionManager()); @@ -448,6 +458,11 @@ public UpdateResult updateEntity(T updatedEntity, EntityPersistAdapter.Change @Override public UpdateResult updateEntity(JsonNode updatedEntityJson) throws Exception { + log.trace("updateEntity 1 parm"); + if( isReadOnly()){ + log.error("Trying to update a {} when service is read-only", getFriendlyName()); + throw new RuntimeException("Please use the parent object to update a " + getFriendlyName()); + } TransactionTemplate transactionTemplate = new TransactionTemplate(this.getTransactionManager()); @@ -783,4 +798,22 @@ protected Optional flexLookupIdOnly(String someKindOfId){ */ @Transactional public abstract Optional flexLookup(String someKindOfId); + + @Override + public boolean isReadOnly() { + return readOnly; + } + + private String getFriendlyName() { + if( getEntityClass() != null ){ + String fullClassName = getEntityClass().getName(); + if( fullClassName.contains(".")) { + return fullClassName.substring(fullClassName.lastIndexOf(".")+1); + } + return fullClassName; + } + String name = getContext(); + if( name.endsWith("s")) return name.substring(0, name.length()-1); + return name; + } } diff --git a/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/service/GsrsEntityService.java b/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/service/GsrsEntityService.java index d3bbf8d0..69af6222 100644 --- a/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/service/GsrsEntityService.java +++ b/gsrs-spring-boot-autoconfigure/src/main/java/gsrs/service/GsrsEntityService.java @@ -12,14 +12,12 @@ import lombok.Builder; import lombok.Data; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.UUID; /** * Contains all the business logic for converting JSON into an Entity, reading and writing @@ -157,4 +155,6 @@ public static ProcessResult ofUpdate(UpdateResult update) { return builder.build(); } } + + boolean isReadOnly(); }