From c5222a72ef11bd440f5c2bd017e820922ff6735a Mon Sep 17 00:00:00 2001 From: danielhanrahan Date: Wed, 28 Feb 2024 16:05:07 +0000 Subject: Add bearer token to NCMP passthrough operations (CPS-2126 #2) For NCMP resource data passthrough operations, accept an authorization header and propagate it to outgoing DMI request if it has a bearer token, otherwise use same behaviour as before Issue-ID: CPS-2128 Change-Id: Ib3bf401abce4221a8b706989fb6f07618aa33fe2 Signed-off-by: danielhanrahan --- cps-ncmp-rest/docs/openapi/components.yaml | 7 +++++ cps-ncmp-rest/docs/openapi/ncmp.yml | 8 ++++- .../rest/controller/NetworkCmProxyController.java | 35 ++++++++++++++-------- .../handlers/NcmpCachedResourceRequestHandler.java | 3 +- .../handlers/NcmpDatastoreRequestHandler.java | 17 +++++++---- .../NcmpPassthroughResourceRequestHandler.java | 6 ++-- .../controller/NetworkCmProxyControllerSpec.groovy | 13 ++++---- .../NcmpDatastoreRequestHandlerSpec.groovy | 6 ++-- 8 files changed, 64 insertions(+), 31 deletions(-) (limited to 'cps-ncmp-rest') diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml index 6b53292af..cd77effc8 100644 --- a/cps-ncmp-rest/docs/openapi/components.yaml +++ b/cps-ncmp-rest/docs/openapi/components.yaml @@ -629,6 +629,13 @@ components: type: string default: application/json example: application/yang-data+json + authorizationParamInHeader: + name: Authorization + in: header + required: false + description: Authorization parameter for request. + schema: + type: string datastoreName: name: datastore-name in: path diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml index 2787a0837..0cb1cdffb 100755 --- a/cps-ncmp-rest/docs/openapi/ncmp.yml +++ b/cps-ncmp-rest/docs/openapi/ncmp.yml @@ -32,6 +32,7 @@ resourceDataForCmHandle: - $ref: 'components.yaml#/components/parameters/optionsParamInQuery' - $ref: 'components.yaml#/components/parameters/topicParamInQuery' - $ref: 'components.yaml#/components/parameters/includeDescendantsOptionInQuery' + - $ref: 'components.yaml#/components/parameters/authorizationParamInHeader' responses: 200: description: OK @@ -62,6 +63,7 @@ resourceDataForCmHandle: - $ref: 'components.yaml#/components/parameters/cmHandleInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' + - $ref: 'components.yaml#/components/parameters/authorizationParamInHeader' requestBody: required: true content: @@ -100,6 +102,7 @@ resourceDataForCmHandle: - $ref: 'components.yaml#/components/parameters/cmHandleInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' + - $ref: 'components.yaml#/components/parameters/authorizationParamInHeader' requestBody: required: true content: @@ -138,6 +141,7 @@ resourceDataForCmHandle: - $ref: 'components.yaml#/components/parameters/cmHandleInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' + - $ref: 'components.yaml#/components/parameters/authorizationParamInHeader' requestBody: required: true content: @@ -170,6 +174,7 @@ resourceDataForCmHandle: - $ref: 'components.yaml#/components/parameters/cmHandleInPath' - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' - $ref: 'components.yaml#/components/parameters/contentParamInHeader' + - $ref: 'components.yaml#/components/parameters/authorizationParamInHeader' responses: 204: $ref: 'components.yaml#/components/responses/NoContent' @@ -193,6 +198,7 @@ dataOperationForCmHandle: operationId: executeDataOperationForCmHandles parameters: - $ref: 'components.yaml#/components/parameters/requiredTopicParamInQuery' + - $ref: 'components.yaml#/components/parameters/authorizationParamInHeader' requestBody: required: true content: @@ -469,4 +475,4 @@ setDataSyncEnabledFlag: 500: $ref: 'components.yaml#/components/responses/InternalServerError' 502: - $ref: 'components.yaml#/components/responses/BadGateway' \ No newline at end of file + $ref: 'components.yaml#/components/responses/BadGateway' diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java index 6ec24448d..1c6aaf226 100755 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java @@ -94,6 +94,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * @param optionsParamInQuery options query parameter * @param topicParamInQuery topic query parameter * @param includeDescendants whether to include descendants or not + * @param authorization contents of Authorization header, or null if not present * @return {@code ResponseEntity} response from dmi plugin */ @Override @@ -103,16 +104,17 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String resourceIdentifier, final String optionsParamInQuery, final String topicParamInQuery, - final Boolean includeDescendants) { + final Boolean includeDescendants, + final String authorization) { final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler = getNcmpDatastoreRequestHandler(datastoreName); return ncmpDatastoreRequestHandler.executeRequest(datastoreName, cmHandle, resourceIdentifier, - optionsParamInQuery, topicParamInQuery, includeDescendants); + optionsParamInQuery, topicParamInQuery, includeDescendants, authorization); } @Override public ResponseEntity executeDataOperationForCmHandles(final String topicParamInQuery, - final DataOperationRequest - dataOperationRequest) { + final DataOperationRequest dataOperationRequest, + final String authorization) { return ncmpPassthroughResourceRequestHandler.executeRequest(topicParamInQuery, dataOperationRequestMapper.toDataOperationRequest(dataOperationRequest)); } @@ -148,6 +150,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * @param resourceIdentifier resource identifier * @param requestBody the request body * @param contentType content type of body + * @param authorization contents of Authorization header, or null if not present * @return {@code ResponseEntity} response from dmi plugin */ @@ -156,14 +159,15 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String cmHandle, final String resourceIdentifier, final Object requestBody, - final String contentType) { + final String contentType, + final String authorization) { validateDataStore(PASSTHROUGH_RUNNING, datastoreName); final Object responseObject = networkCmProxyDataService .writeResourceDataPassThroughRunningForCmHandle( cmHandle, resourceIdentifier, PATCH, - jsonObjectMapper.asJsonString(requestBody), contentType); + jsonObjectMapper.asJsonString(requestBody), contentType, authorization); return ResponseEntity.ok(responseObject); } @@ -175,6 +179,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * @param resourceIdentifier resource identifier * @param requestBody the request body * @param contentType content type of body + * @param authorization contents of Authorization header, or null if not present * @return {@code ResponseEntity} response from dmi plugin */ @Override @@ -182,12 +187,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String cmHandle, final String resourceIdentifier, final Object requestBody, - final String contentType) { - + final String contentType, + final String authorization) { validateDataStore(PASSTHROUGH_RUNNING, datastoreName); networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, - resourceIdentifier, CREATE, jsonObjectMapper.asJsonString(requestBody), contentType); + resourceIdentifier, CREATE, jsonObjectMapper.asJsonString(requestBody), contentType, authorization); return new ResponseEntity<>(HttpStatus.CREATED); } @@ -199,6 +204,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * @param resourceIdentifier resource identifier * @param requestBody the request body * @param contentType content type of the body + * @param authorization contents of Authorization header, or null if not present * @return response entity */ @@ -207,11 +213,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String cmHandle, final String resourceIdentifier, final Object requestBody, - final String contentType) { + final String contentType, + final String authorization) { validateDataStore(PASSTHROUGH_RUNNING, datastoreName); networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, - resourceIdentifier, UPDATE, jsonObjectMapper.asJsonString(requestBody), contentType); + resourceIdentifier, UPDATE, jsonObjectMapper.asJsonString(requestBody), contentType, authorization); return new ResponseEntity<>(HttpStatus.OK); } @@ -222,18 +229,20 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { * @param cmHandle cm handle identifier * @param resourceIdentifier resource identifier * @param contentType content type of the body + * @param authorization contents of Authorization header, or null if not present * @return response entity no content if request is successful */ @Override public ResponseEntity deleteResourceDataRunningForCmHandle(final String datastoreName, final String cmHandle, final String resourceIdentifier, - final String contentType) { + final String contentType, + final String authorization) { validateDataStore(PASSTHROUGH_RUNNING, datastoreName); networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, - resourceIdentifier, DELETE, NO_BODY, contentType); + resourceIdentifier, DELETE, NO_BODY, contentType, authorization); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java index 85a1eae23..430c0996f 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java @@ -74,7 +74,8 @@ public class NcmpCachedResourceRequestHandler extends NcmpDatastoreRequestHandle final String optionsParamInQuery, final String topicParamInQuery, final String requestId, - final boolean includeDescendants) { + final boolean includeDescendants, + final String authorization) { final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendants); diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java index d40ab9b39..8b0809090 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java @@ -56,6 +56,7 @@ public abstract class NcmpDatastoreRequestHandler { * @param optionsParamInQuery the options param in query * @param topicParamInQuery the topic param in query * @param includeDescendants whether include descendants + * @param authorization contents of Authorization header, or null if not present * @return the response entity */ public ResponseEntity executeRequest(final String datastoreName, @@ -63,12 +64,13 @@ public abstract class NcmpDatastoreRequestHandler { final String resourceIdentifier, final String optionsParamInQuery, final String topicParamInQuery, - final boolean includeDescendants) { + final boolean includeDescendants, + final String authorization) { final boolean asyncResponseRequested = topicParamInQuery != null; if (asyncResponseRequested && notificationFeatureEnabled) { return executeAsyncTaskAndGetResponseEntity(datastoreName, cmHandleId, resourceIdentifier, - optionsParamInQuery, topicParamInQuery, includeDescendants); + optionsParamInQuery, topicParamInQuery, includeDescendants, authorization); } if (asyncResponseRequested) { @@ -76,7 +78,7 @@ public abstract class NcmpDatastoreRequestHandler { + "will use synchronous operation."); } final Supplier taskSupplier = getTaskSupplierForGetRequest(datastoreName, cmHandleId, - resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID, includeDescendants); + resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID, includeDescendants, authorization); return executeTaskSync(taskSupplier); } @@ -99,10 +101,12 @@ public abstract class NcmpDatastoreRequestHandler { final String resourceIdentifier, final String optionsParamInQuery, final String topicParamInQuery, - final boolean includeDescendants) { + final boolean includeDescendants, + final String authorization) { final String requestId = UUID.randomUUID().toString(); final Supplier taskSupplier = getTaskSupplierForGetRequest(datastoreName, cmHandleId, - resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId, includeDescendants); + resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId, includeDescendants, + authorization); return executeTaskAsync(topicParamInQuery, requestId, taskSupplier); } @@ -112,6 +116,7 @@ public abstract class NcmpDatastoreRequestHandler { final String optionsParamInQuery, final String topicParamInQuery, final String requestId, - final boolean includeDescendant); + final boolean includeDescendant, + final String authorization); } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java index 8a3257576..5da8c9120 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java @@ -81,10 +81,12 @@ public class NcmpPassthroughResourceRequestHandler extends NcmpDatastoreRequestH final String optionsParamInQuery, final String topicParamInQuery, final String requestId, - final boolean includeDescendants) { + final boolean includeDescendants, + final String authorization) { return () -> networkCmProxyDataService.getResourceDataForCmHandle( - datastoreName, cmHandleId, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId); + datastoreName, cmHandleId, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId, + authorization); } private ResponseEntity getRequestIdAndSendDataOperationRequestToDmiService(final String topicParamInQuery, diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy index 983f2438c..dba2b30fd 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy @@ -135,6 +135,7 @@ class NetworkCmProxyControllerSpec extends Specification { @Shared def NO_TOPIC = null def NO_REQUEST_ID = null + def NO_AUTH_HEADER = null def TIMOUT_FOR_TEST = 1234 def logger = Spy(ListAppender) @@ -162,7 +163,7 @@ class NetworkCmProxyControllerSpec extends Specification { ).andReturn().response then: 'the NCMP data service is called with getResourceDataOperationalForCmHandle' 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.datastoreName, 'testCmHandle', - 'parent/child','(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID) + 'parent/child','(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) and: 'response status is Ok' response.status == HttpStatus.OK.value() } @@ -279,7 +280,7 @@ class NetworkCmProxyControllerSpec extends Specification { "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)" and: 'ncmp service returns json object' mockNetworkCmProxyDataService.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', - resourceIdentifier,'(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID) >> '{valid-json}' + resourceIdentifier,'(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER) >> '{valid-json}' when: 'get data resource request is performed' def response = mvc.perform( get(getUrl) @@ -310,7 +311,7 @@ class NetworkCmProxyControllerSpec extends Specification { ).andReturn().response then: 'ncmp service method to update resource is called' 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', - 'parent/child', UPDATE, requestBody, 'application/json;charset=UTF-8') + 'parent/child', UPDATE, requestBody, 'application/json;charset=UTF-8', NO_AUTH_HEADER) and: 'the response status is OK' response.status == HttpStatus.OK.value() } @@ -326,7 +327,7 @@ class NetworkCmProxyControllerSpec extends Specification { ).andReturn().response then: 'ncmp service method to create resource called' 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', - 'parent/child', CREATE, requestBody, 'application/json;charset=UTF-8') + 'parent/child', CREATE, requestBody, 'application/json;charset=UTF-8', NO_AUTH_HEADER) and: 'resource is created' response.status == HttpStatus.CREATED.value() } @@ -492,7 +493,7 @@ class NetworkCmProxyControllerSpec extends Specification { ).andReturn().response then: 'ncmp service method to update resource is called' 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', - 'parent/child', PATCH, requestBody, 'application/json;charset=UTF-8') + 'parent/child', PATCH, requestBody, 'application/json;charset=UTF-8', NO_AUTH_HEADER) and: 'the response status is OK' response.status == HttpStatus.OK.value() } @@ -507,7 +508,7 @@ class NetworkCmProxyControllerSpec extends Specification { .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andReturn().response then: 'the ncmp service method to delete resource is called (with null as body)' 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', - 'parent/child', DELETE, null, 'application/json;charset=UTF-8') + 'parent/child', DELETE, null, 'application/json;charset=UTF-8', NO_AUTH_HEADER) and: 'the response is No Content' response.status == HttpStatus.NO_CONTENT.value() } diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerSpec.groovy index 4edbf3569..328a85e71 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerSpec.groovy @@ -37,6 +37,8 @@ class NcmpDatastoreRequestHandlerSpec extends Specification { def objectUnderTest = new NcmpPassthroughResourceRequestHandler(spiedCpsNcmpTaskExecutor, mockNetworkCmProxyDataService) + def NO_AUTH_HEADER = null + def setup() { objectUnderTest.timeOutInMilliSeconds = 100 } @@ -47,11 +49,11 @@ class NcmpDatastoreRequestHandlerSpec extends Specification { and: 'a flag to track the network service call' def networkServiceMethodCalled = false and: 'the (mocked) service will use the flag to indicate if it is called' - mockNetworkCmProxyDataService.getResourceDataForCmHandle('ds', 'ch1', 'resource1', 'options', _, _) >> { + mockNetworkCmProxyDataService.getResourceDataForCmHandle('ds', 'ch1', 'resource1', 'options', _, _, NO_AUTH_HEADER) >> { networkServiceMethodCalled = true } when: 'get request is executed with topic = #topic' - objectUnderTest.executeRequest('ds', 'ch1', 'resource1', 'options', topic, false) + objectUnderTest.executeRequest('ds', 'ch1', 'resource1', 'options', topic, false, NO_AUTH_HEADER) then: 'the task is executed in an async fashion or not' expectedCalls * spiedCpsNcmpTaskExecutor.executeTask(*_) and: 'the service request is invoked' -- cgit 1.2.3-korg