Forward authorization header from ProvMnS to Policy Executor 29/142929/4
authorseanbeirne <sean.beirne@est.tech>
Wed, 14 Jan 2026 16:00:46 +0000 (16:00 +0000)
committerSeán Beirne <sean.beirne@est.tech>
Wed, 14 Jan 2026 17:13:15 +0000 (17:13 +0000)
- Forwards with no validation to Policy Executor
- Logs authorization in policy executor stub

Issue-ID: CPS-3129
Change-Id: I74014bba3848534df42262a9a8397cd99d5ac2cb
Signed-off-by: seanbeirne <sean.beirne@est.tech>
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnSController.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/ProvMnSControllerSpec.groovy
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/provmns/ParameterHelper.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/provmns/RequestParameters.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/OperationDetailsFactorySpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/provmns/ParameterHelperSpec.groovy
policy-executor-stub/src/main/java/org/onap/cps/policyexecutor/stub/controller/PolicyExecutorStubController.java

index 7de3891..4cdbc9f 100644 (file)
@@ -118,7 +118,8 @@ public class ProvMnSController implements ProvMnS {
         final RequestParameters requestParameters = ParameterHelper.extractRequestParameters(httpServletRequest);
         try {
             final YangModelCmHandle yangModelCmHandle = getAndValidateYangModelCmHandle(requestParameters);
-            checkPermissionForEachPatchItem(requestParameters.fdn(), patchItems, yangModelCmHandle);
+            checkPermissionForEachPatchItem(requestParameters.fdn(), patchItems,
+                yangModelCmHandle, requestParameters.authorization());
             final UrlTemplateParameters urlTemplateParameters =
                 parametersBuilder.createUrlTemplateParametersForWrite(yangModelCmHandle, requestParameters.fdn());
             return dmiRestClient.synchronousPatchOperation(DATA, patchItems, urlTemplateParameters,
@@ -135,7 +136,7 @@ public class ProvMnSController implements ProvMnS {
             final YangModelCmHandle yangModelCmHandle = getAndValidateYangModelCmHandle(requestParameters);
             final OperationDetails operationDetails =
                 operationDetailsFactory.buildOperationDetails(CREATE, requestParameters, resource);
-            checkPermission(yangModelCmHandle, operationDetails);
+            checkPermission(yangModelCmHandle, operationDetails, requestParameters);
             final UrlTemplateParameters urlTemplateParameters =
                 parametersBuilder.createUrlTemplateParametersForWrite(yangModelCmHandle, requestParameters.fdn());
             return dmiRestClient.synchronousPutOperation(DATA, resource, urlTemplateParameters);
@@ -151,7 +152,7 @@ public class ProvMnSController implements ProvMnS {
             final YangModelCmHandle yangModelCmHandle = getAndValidateYangModelCmHandle(requestParameters);
             final OperationDetails operationDetails =
                 operationDetailsFactory.buildOperationDetailsForDelete(requestParameters.fdn());
-            checkPermission(yangModelCmHandle, operationDetails);
+            checkPermission(yangModelCmHandle, operationDetails, requestParameters);
             final UrlTemplateParameters urlTemplateParameters =
                 parametersBuilder.createUrlTemplateParametersForWrite(yangModelCmHandle, requestParameters.fdn());
             return dmiRestClient.synchronousDeleteOperation(DATA, urlTemplateParameters);
@@ -182,27 +183,30 @@ public class ProvMnSController implements ProvMnS {
     }
 
     private void checkPermission(final YangModelCmHandle yangModelCmHandle,
-                                 final OperationDetails operationDetails) {
+                                 final OperationDetails operationDetails,
+                                 final RequestParameters requestParameters) {
         final Map<String, List<ClassInstance>> changeRequestAsMap = new HashMap<>(1);
         changeRequestAsMap.put(operationDetails.className(), operationDetails.ClassInstances());
         final String changeRequestAsJson = jsonObjectMapper.asJsonString(changeRequestAsMap);
         final int index = yangModelCmHandle.getAlternateId().length();
         final String resourceIdentifier = operationDetails.parentFdn().substring(index);
         policyExecutor.checkPermission(yangModelCmHandle, operationDetails.operationType(),
-            NO_AUTHORIZATION, resourceIdentifier, changeRequestAsJson);
+            requestParameters.authorization(), resourceIdentifier, changeRequestAsJson);
     }
 
     private void checkPermissionForEachPatchItem(final String baseFdn,
                                                  final List<PatchItem> patchItems,
-                                                 final YangModelCmHandle yangModelCmHandle) {
+                                                 final YangModelCmHandle yangModelCmHandle,
+                                                 final String authorization) {
         int patchItemCounter = 0;
         for (final PatchItem patchItem : patchItems) {
             final String extendedPath = baseFdn + patchItem.getPath();
-            final RequestParameters requestParameters = ParameterHelper.createRequestParametersForPatch(extendedPath);
+            final RequestParameters requestParameters =
+                ParameterHelper.createRequestParametersForPatch(extendedPath, authorization);
             final OperationDetails operationDetails =
                 operationDetailsFactory.buildOperationDetails(requestParameters, patchItem);
             try {
-                checkPermission(yangModelCmHandle, operationDetails);
+                checkPermission(yangModelCmHandle, operationDetails, requestParameters);
                 patchItemCounter++;
             } catch (final Exception exception) {
                 final String httpMethodName = "PATCH";
index b628822..e956410 100644 (file)
@@ -234,6 +234,7 @@ class ProvMnSControllerSpec extends Specification {
             mockDmiRestClient.synchronousPatchOperation(*_) >> new ResponseEntity<>('content from DMI', responseStatusFromDmi)
         when: 'patch request is performed'
             def response = mvc.perform(patch(provmnsUrl)
+                    .header('Authorization', 'my authorization')
                     .contentType(contentMediaType)
                     .content(jsonBody))
                     .andReturn().response
@@ -242,7 +243,7 @@ class ProvMnSControllerSpec extends Specification {
         and: 'the response contains the expected content'
             assert response.contentAsString.contains('content from DMI')
         and: 'policy executor was invoked with the expected parameters'
-            1 * mockPolicyExecutor.checkPermission(validCmHandle, OperationType.UPDATE, _, expectedResourceIdForPolicyExecutor, expectedChangeRequest)
+            1 * mockPolicyExecutor.checkPermission(validCmHandle, OperationType.UPDATE, 'my authorization', expectedResourceIdForPolicyExecutor, expectedChangeRequest)
         where: 'following scenarios are applied'
             scenario          | contentMediaType   | jsonBody             | responseStatusFromDmi || expectedResponseStatusFromProvMnS
             'happy flow 3gpp' | patchMediaType3gpp | patchJsonBody3gpp    | OK                    || OK
@@ -299,13 +300,14 @@ class ProvMnSControllerSpec extends Specification {
             def expectedResourceIdentifier = '/myClass=id1/childClass=1'
         when: 'patch data resource request is performed'
             def response = mvc.perform(patch(url)
+                .header('Authorization', 'my authorization')
                 .contentType(patchMediaType)
                 .content('[{"op":"remove","path":"/childClass=1/grandchildClass=1"}]'))
                 .andReturn().response
         then: 'response status is OK'
             assert response.status == OK.value()
         and: 'Policy Executor was invoked with correct details'
-            1 * mockPolicyExecutor.checkPermission(_, OperationType.DELETE, _, expectedResourceIdentifier, expectedDeleteChangeRequest)
+            1 * mockPolicyExecutor.checkPermission(validCmHandle, OperationType.DELETE, 'my authorization', expectedResourceIdentifier, expectedDeleteChangeRequest)
     }
 
     def 'Patch request with no permission from Coordination Management (aka Policy Executor).'() {
@@ -386,6 +388,7 @@ class ProvMnSControllerSpec extends Specification {
             def expectedChangeRequest = '{"grandChildClass":[{"id":"2","attributes":{"attr1":"value1"}}]}'
         when: 'put data resource request is performed'
             def response = mvc.perform(put(putUrl)
+                    .header('Authorization', 'my authorization')
                     .contentType(MediaType.APPLICATION_JSON)
                     .content(resourceAsJson))
                     .andReturn().response
@@ -394,7 +397,7 @@ class ProvMnSControllerSpec extends Specification {
         and: 'the content is whatever the DMI returned'
             assert response.contentAsString == responseContentFromDmi
         and: 'The policy executor was invoked with the expected parameters'
-            1 * mockPolicyExecutor.checkPermission(_, OperationType.CREATE, _, expectedResourceIdentifier, expectedChangeRequest)
+            1 * mockPolicyExecutor.checkPermission(validCmHandle, OperationType.CREATE, 'my authorization', expectedResourceIdentifier, expectedChangeRequest)
         where: 'following responses returned by DMI'
             scenario         | responseStatusFromDmi | responseContentFromDmi
             'happy flow'     | OK                    | 'content from DMI'
@@ -427,13 +430,13 @@ class ProvMnSControllerSpec extends Specification {
         and: 'dmi provides a response'
             mockDmiRestClient.synchronousDeleteOperation(*_) >> new ResponseEntity<>(responseContentFromDmi, responseStatusFromDmi)
         when: 'Delete data resource request is performed'
-            def response = mvc.perform(delete(deleteUrl)).andReturn().response
+            def response = mvc.perform(delete(deleteUrl).header('Authorization', 'my authorization')).andReturn().response
         then: 'response status is the same as what DMI gave'
             assert response.status == responseStatusFromDmi.value()
         and: 'the content is whatever the DMI returned'
             assert response.contentAsString == responseContentFromDmi
         and: 'Policy Executor was invoked with correct resource identifier and almost empty operation details (not used for delete!)'
-            1 * mockPolicyExecutor.checkPermission(_, OperationType.DELETE, _, '/myClass=id1/childClass=1', expectedDeleteChangeRequest)
+            1 * mockPolicyExecutor.checkPermission(_, OperationType.DELETE, 'my authorization', '/myClass=id1/childClass=1', expectedDeleteChangeRequest)
         where: 'following responses returned by DMI'
             scenario         | responseStatusFromDmi | responseContentFromDmi
             'happy flow'     | OK                    | 'content from DMI'
index d290385..d326d68 100644 (file)
@@ -48,18 +48,21 @@ public class ParameterHelper {
             throw createProvMnSException(httpServletRequest.getMethod(), uriPath);
         }
         final String fdn = "/" + pathVariables[REQUEST_FDN_INDEX];
-        return createRequestParameters(httpServletRequest.getMethod(), fdn);
+        return createRequestParameters(httpServletRequest.getMethod(),
+            httpServletRequest.getHeader("Authorization"), fdn);
     }
 
     /**
      * Create RequestParameters object for PATCH operations.
      *
      * @param pathWithAttributes the path a fdn possibly with containing attributes
+     * @param authorization HttpServletRequest object
      * @return RequestParameters object for PATCH operation
      */
-    public static RequestParameters createRequestParametersForPatch(final String pathWithAttributes) {
+    public static RequestParameters createRequestParametersForPatch(
+        final String pathWithAttributes, final String authorization) {
         final String fdn = removeTrailingHash(extractFdn(pathWithAttributes));
-        return createRequestParameters("PATCH", fdn);
+        return createRequestParameters("PATCH", authorization, fdn);
     }
 
     /**
@@ -98,6 +101,7 @@ public class ParameterHelper {
     }
 
     private static RequestParameters createRequestParameters(final String httpMethodName,
+                                                             final String authorization,
                                                              final String fdn) {
         final int lastSlashIndex = fdn.lastIndexOf('/');
         final String classNameAndId;
@@ -110,7 +114,7 @@ public class ParameterHelper {
         }
         final String className = splitClassNameId[0];
         final String id = removeTrailingHash(splitClassNameId[1]);
-        return new RequestParameters(httpMethodName, fdn, uriLdnFirstPart, className, id);
+        return new RequestParameters(httpMethodName, authorization, fdn, uriLdnFirstPart, className, id);
     }
 
     private static ProvMnSException createProvMnSException(final String httpMethodName, final String uriPath) {
index fb823d4..82adaec 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.cps.ncmp.impl.provmns;
 
 public record RequestParameters(
     String httpMethodName,
+    String authorization,
     String fdn,
     String uriLdnFirstPart,
     String className,
index 19b16ee..31e7ea4 100644 (file)
@@ -35,7 +35,7 @@ import static org.onap.cps.ncmp.api.data.models.OperationType.UPDATE
 class OperationDetailsFactorySpec extends Specification {
 
     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
-    def requestPathParameters = new RequestParameters('some method', '/parent=id1/someChild=someId','some uri', 'class from uri', 'id from uri')
+    def requestPathParameters = new RequestParameters('some method', 'some authorization', '/parent=id1/someChild=someId','some uri', 'class from uri', 'id from uri')
 
     OperationDetailsFactory objectUnderTest = new OperationDetailsFactory(jsonObjectMapper)
 
index eb8cd11..08b37a0 100644 (file)
@@ -51,7 +51,7 @@ class ParameterHelperSpec extends Specification {
 
     def 'Extract request parameters for Patch Path with attributes.'() {
         when: 'the request parameters are extracted from the path'
-            def result = objectUnderTest.createRequestParametersForPatch(path)
+            def result = objectUnderTest.createRequestParametersForPatch(path, 'some authorization')
         then: 'the FDN is as expected'
             assert result.fdn == expectedFdn
         and: 'the class name and id are mapped correctly'
index 91348a0..aa4d8b0 100644 (file)
@@ -58,6 +58,7 @@ public class PolicyExecutorStubController implements OperationPermissionApi {
                                                                         final String accept,
                                                                         final String authorization) {
         log.info("Stub Policy Executor Invoked");
+        log.info("Authorization sent with request: {}", authorization);
         log.info("Permission Request: {}", formatPermissionRequest(permissionRequest));
         if (permissionRequest.getOperations().isEmpty()) {
             return new ResponseEntity<>(HttpStatus.BAD_REQUEST);