From ae653157efa185300c8c57183b3a2849534472dc Mon Sep 17 00:00:00 2001 From: seanbeirne Date: Fri, 28 Nov 2025 11:22:09 +0000 Subject: [PATCH] Limit number of operations in a ProvMnS PATCH request -New configurable stting in application.yml Issue-ID: CPS-3070 Change-Id: Ia41ec57cd72a19da989b85b3dbd88e36b2f2dc5b Signed-off-by: seanbeirne --- cps-application/src/main/resources/application.yml | 2 ++ .../ncmp/rest/controller/ProvMnsController.java | 9 +++++++++ .../ncmp/rest/provmns/ErrorResponseBuilder.java | 3 ++- .../rest/controller/ProvMnsControllerSpec.groovy | 23 +++++++++++++++++++++- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml index bfc5339338..e3e4261eac 100644 --- a/cps-application/src/main/resources/application.yml +++ b/cps-application/src/main/resources/application.yml @@ -107,6 +107,8 @@ app: cm-subscription-dmi-out: ${CM_SUBSCRIPTION_DMI_OUT_TOPIC:dmi-ncmp-cm-avc-subscription} cm-events-topic: ${NCMP_CM_EVENTS_TOPIC:cm-events} inventory-events-topic: ncmp-inventory-events + provmns: + max-patch-operations: 10 lcm: events: topic: ${LCM_EVENTS_TOPIC:ncmp-events} diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnsController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnsController.java index 9ea71de586..1bad56bb8e 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnsController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnsController.java @@ -44,6 +44,7 @@ import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher; import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; import org.onap.cps.ncmp.rest.provmns.ErrorResponseBuilder; import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; @@ -68,6 +69,9 @@ public class ProvMnsController implements ProvMnS { private final JsonObjectMapper jsonObjectMapper; private final OperationDetailsFactory operationDetailsFactory; + @Value("${app.ncmp.provmns.max-patch-operations:10}") + private Integer maxNumberOfPatchOperations; + @Override public ResponseEntity getMoi(final HttpServletRequest httpServletRequest, final Scope scope, @@ -98,6 +102,11 @@ public class ProvMnsController implements ProvMnS { @Override public ResponseEntity patchMoi(final HttpServletRequest httpServletRequest, final List patchItems) { + if (patchItems.size() > maxNumberOfPatchOperations) { + return errorResponseBuilder.buildErrorResponsePatch(HttpStatus.PAYLOAD_TOO_LARGE, + patchItems.size() + " operations in request, this exceeds the maximum of " + + maxNumberOfPatchOperations); + } final RequestPathParameters requestPathParameters = parameterMapper.extractRequestParameters(httpServletRequest); try { diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/provmns/ErrorResponseBuilder.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/provmns/ErrorResponseBuilder.java index 50b99c25d3..ac0a9e44d7 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/provmns/ErrorResponseBuilder.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/provmns/ErrorResponseBuilder.java @@ -34,7 +34,8 @@ public class ErrorResponseBuilder { private static final Map ERROR_MAP = Map.of( HttpStatus.NOT_FOUND, "IE_NOT_FOUND", HttpStatus.NOT_ACCEPTABLE, "APPLICATION_LAYER_ERROR", - HttpStatus.UNPROCESSABLE_ENTITY, "SERVER_LIMITATION" + HttpStatus.UNPROCESSABLE_ENTITY, "SERVER_LIMITATION", + HttpStatus.PAYLOAD_TOO_LARGE, "SERVER_LIMITATION" ); /** diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/ProvMnsControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/ProvMnsControllerSpec.groovy index b06e407eee..929185504b 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/ProvMnsControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/ProvMnsControllerSpec.groovy @@ -92,6 +92,9 @@ class ProvMnsControllerSpec extends Specification { @Value('${rest.api.provmns-base-path}') def provMnSBasePath + @Value('${app.ncmp.provmns.max-patch-operations:10}') + Integer maxNumberOfPatchOperations + def 'Get resource data request where #scenario.'() { given: 'resource data url' def getUrl = "$provMnSBasePath/v1/someUriLdnFirstPart/someClassName=someId" @@ -200,7 +203,7 @@ class ProvMnsControllerSpec extends Specification { inventoryPersistence.getYangModelCmHandle('cm-1') >> new YangModelCmHandle(id:'cm-1', dmiServiceName: 'someDmiService', dataProducerIdentifier: 'someDataProducerId', compositeState: new CompositeState(cmHandleState: READY)) and: 'policy executor throws exception (denied)' policyExecutor.checkPermission(*_) >> {throw new RuntimeException()} - when: 'put data resource request is performed' + when: 'patch data resource request is performed' def response = mvc.perform(patch(url) .contentType(new MediaType('application', 'json-patch+json')) .content(patchJsonBody)) @@ -209,6 +212,24 @@ class ProvMnsControllerSpec extends Specification { assert response.status == HttpStatus.NOT_ACCEPTABLE.value() } + def 'Patch request with too many operations.'() { + given: 'resource data url' + def url = "$provMnSBasePath/v1/someUriLdnFirstPart/someClassName=someId" + and: 'a patch request with more operations than the max allowed' + def patchItems = [] + for (def i = 0; i <= maxNumberOfPatchOperations; i++) { + patchItems.add(new PatchItem(op: 'REMOVE', path: 'someUriLdnFirstPart')) + } + def patchItemsJsonRequestBody = jsonObjectMapper.asJsonString(patchItems) + when: 'patch data resource request is performed' + def response = mvc.perform(patch(url) + .contentType(new MediaType('application', 'json-patch+json')) + .content(patchItemsJsonRequestBody)) + .andReturn().response + then: 'response status is PAYLOAD_TOO_LARGE (413)' + assert response.status == HttpStatus.PAYLOAD_TOO_LARGE.value() + } + def 'Put resource data request where #scenario.'() { given: 'resource data url' def putUrl = "$provMnSBasePath/v1/someUriLdnFirstPart/someClassName=someId" -- 2.16.6