summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToineSiebelink <toine.siebelink@est.tech>2024-07-10 17:34:13 +0100
committerToineSiebelink <toine.siebelink@est.tech>2024-07-11 16:41:46 +0100
commitef08e0fcf7a8c507ccd0e5c6f6ed8d43e9583370 (patch)
treee4ec5eb32df022b04e43691064bbb419014e34f9
parentdd1b77b906052911b4883c7fa515ca1f2f2e9fda (diff)
Policy Executor Feature Toggle
- defined config parameters for feature toggle and server details - log request details when feature enabled - improved JavaDoc in Controller - improved configuration properties checks in HttpClientConfigurationSpec Issue-ID: CPS-2311 Change-Id: I1ff4bd3592ce2570ac74f9ec6e62b75001cb611a Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
-rwxr-xr-xcps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java18
-rw-r--r--cps-ncmp-rest/src/test/resources/application.yml2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java4
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/PolicyExecutor.java74
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/HttpClientConfigurationSpec.groovy8
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy8
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy73
-rw-r--r--cps-ncmp-service/src/test/resources/application.yml7
8 files changed, 178 insertions, 16 deletions
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 bb2332d615..a482cf5a3c 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
@@ -121,7 +121,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
/**
* Query resource data from datastore.
*
- * @param datastoreName name of the datastore
+ * @param datastoreName name of the datastore (currently only supports "ncmp-datastore:operational")
* @param cmHandle cm handle identifier
* @param cpsPath CPS Path
* @param optionsParamInQuery options query parameter
@@ -144,9 +144,9 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
}
/**
- * Patch resource data from passthrough-running.
+ * Patch resource data.
*
- * @param datastoreName name of the datastore
+ * @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running")
* @param cmHandle cm handle identifier
* @param resourceIdentifier resource identifier
* @param requestBody the request body
@@ -173,9 +173,9 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
}
/**
- * Create resource data in datastore pass-through running for given cm-handle.
+ * Create resource data for given cm-handle.
*
- * @param datastoreName name of the datastore
+ * @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running")
* @param cmHandle cm handle identifier
* @param resourceIdentifier resource identifier
* @param requestBody the request body
@@ -198,9 +198,9 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
}
/**
- * Update resource data in datastore pass-through running for given cm-handle.
+ * Update resource data for given cm-handle.
*
- * @param datastoreName name of the datastore
+ * @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running")
* @param cmHandle cm handle identifier
* @param resourceIdentifier resource identifier
* @param requestBody the request body
@@ -224,9 +224,9 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
}
/**
- * Delete resource data in datastore pass-through running for a given cm-handle.
+ * Delete resource data for a given cm-handle.
*
- * @param datastoreName name of the datastore
+ * @param datastoreName name of the datastore (currently only supports "ncmp-datastore:passthrough-running")
* @param cmHandle cm handle identifier
* @param resourceIdentifier resource identifier
* @param contentType content type of the body
diff --git a/cps-ncmp-rest/src/test/resources/application.yml b/cps-ncmp-rest/src/test/resources/application.yml
index 9df1e580f6..aa5716716b 100644
--- a/cps-ncmp-rest/src/test/resources/application.yml
+++ b/cps-ncmp-rest/src/test/resources/application.yml
@@ -26,4 +26,4 @@ notification:
enabled: true
async:
executor:
- time-out-value-in-ms: 2000 \ No newline at end of file
+ time-out-value-in-ms: 2000
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
index b902fe2767..4cbf9d4b3b 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
@@ -71,6 +71,7 @@ public class DmiDataOperations {
private final JsonObjectMapper jsonObjectMapper;
private final DmiProperties dmiProperties;
private final DmiRestClient dmiRestClient;
+ private final PolicyExecutor policyExecutor;
/**
* This method fetches the resource data from the operational data store for a given CM handle
@@ -170,6 +171,9 @@ public class DmiDataOperations {
final String dataType,
final String authorization) {
final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId);
+
+ policyExecutor.checkPermission(yangModelCmHandle, operationType, authorization, resourceId, requestData);
+
final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/PolicyExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/PolicyExecutor.java
new file mode 100644
index 0000000000..2b5eb9e792
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/PolicyExecutor.java
@@ -0,0 +1,74 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.data;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.data.models.OperationType;
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class PolicyExecutor {
+
+ @Value("${ncmp.policy-executor.enabled:false}")
+ private boolean enabled;
+
+ @Value("${ncmp.policy-executor.server.address:http://policy-executor}")
+ private String serverAddress;
+
+ @Value("${ncmp.policy-executor.server.port:8080}")
+ private String serverPort;
+
+ private static final String PAYLOAD_TYPE_PREFIX = "cm_";
+
+ /**
+ * Use the Policy Executor to check permission for a cm write operation.
+ * Wil throw an exception when the operation is not permitted (work in progress)
+ *
+ * @param yangModelCmHandle the cm handle involved
+ * @param operationType the write operation
+ * @param authorization the original rest authorization token (can be used to determine the client)
+ * @param resourceIdentifier the resource identifier (can be blank)
+ * @param changeRequestAsJson the change details from the original rest request in json format
+ */
+ public void checkPermission(final YangModelCmHandle yangModelCmHandle,
+ final OperationType operationType,
+ final String authorization,
+ final String resourceIdentifier,
+ final String changeRequestAsJson) {
+ if (enabled) {
+ final String payloadType = PAYLOAD_TYPE_PREFIX + operationType.getOperationName();
+ log.info("Policy Executor Enabled");
+ log.info("Address : {}", serverAddress);
+ log.info("Port : {}", serverPort);
+ log.info("Authorization : {}", authorization);
+ log.info("Payload Type : {}", payloadType);
+ log.info("Target FDN : {}", yangModelCmHandle.getAlternateId());
+ log.info("CM Handle Id : {}", yangModelCmHandle.getId());
+ log.info("Resource Identifier : {}", resourceIdentifier);
+ log.info("Change Request (json) : {}", changeRequestAsJson);
+ }
+ }
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/HttpClientConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/HttpClientConfigurationSpec.groovy
index 91aeb88a54..1d3b22fb73 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/HttpClientConfigurationSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/HttpClientConfigurationSpec.groovy
@@ -25,23 +25,21 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ContextConfiguration
-import org.springframework.test.context.TestPropertySource
import spock.lang.Specification
@SpringBootTest
@ContextConfiguration(classes = [HttpClientConfiguration])
-@EnableConfigurationProperties(HttpClientConfiguration.class)
-@TestPropertySource(properties = ["ncmp.dmi.httpclient.data-services.readTimeoutInSeconds=789", "ncmp.dmi.httpclient.model-services.maximumConnectionsTotal=111"])
+@EnableConfigurationProperties(HttpClientConfiguration)
class HttpClientConfigurationSpec extends Specification {
@Autowired
- private HttpClientConfiguration httpClientConfiguration
+ HttpClientConfiguration httpClientConfiguration
def 'Test http client configuration properties of data with custom and default values'() {
expect: 'properties are populated correctly for data'
with(httpClientConfiguration.dataServices) {
assert connectionTimeoutInSeconds == 123
- assert readTimeoutInSeconds == 789
+ assert readTimeoutInSeconds == 33
assert writeTimeoutInSeconds == 30
assert maximumConnectionsTotal == 100
assert pendingAcquireMaxCount == 22
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
index c2bbfc2edc..970444f643 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
@@ -36,6 +36,7 @@ import org.onap.cps.ncmp.utils.TestUtils
import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
@@ -53,7 +54,7 @@ import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATA
import static org.onap.cps.ncmp.utils.events.CloudEventMapper.toTargetEvent
@SpringBootTest
-@ContextConfiguration(classes = [EventsPublisher, CpsApplicationContext, DmiProperties, DmiDataOperations])
+@ContextConfiguration(classes = [EventsPublisher, CpsApplicationContext, DmiProperties, DmiDataOperations, PolicyExecutor])
class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
def NO_TOPIC = null
@@ -72,6 +73,9 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
@SpringBean
EventsPublisher eventsPublisher = Stub()
+ @SpringBean
+ PolicyExecutor policyExecutor = Mock()
+
def 'call get resource data for #expectedDataStore from DMI without topic #scenario.'() {
given: 'a cm handle for #cmHandleId'
mockYangModelCmHandleRetrieval(dmiProperties)
@@ -161,6 +165,8 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
def result = objectUnderTest.writeResourceDataPassThroughRunningFromDmi(cmHandleId, 'parent/child', operation, 'requestData', 'some data type', NO_AUTH_HEADER)
then: 'the result is the response from the DMI service'
assert result == responseFromDmi
+ and: 'the permission was checked with the policy executor'
+ 1 * policyExecutor.checkPermission(_, operation, NO_AUTH_HEADER, resourceIdentifier, 'requestData' )
where: 'the following operation is performed'
operation || expectedOperationInUrl
CREATE || 'create'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy
new file mode 100644
index 0000000000..654206776b
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/PolicyExecutorSpec.groovy
@@ -0,0 +1,73 @@
+package org.onap.cps.ncmp.impl.data
+
+import ch.qos.logback.classic.Level
+import ch.qos.logback.classic.Logger
+import ch.qos.logback.classic.spi.ILoggingEvent
+import ch.qos.logback.core.read.ListAppender
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
+import org.slf4j.LoggerFactory
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.test.context.ContextConfiguration
+import spock.lang.Specification
+
+import static org.onap.cps.ncmp.api.data.models.OperationType.PATCH
+
+@SpringBootTest
+@ContextConfiguration(classes = [PolicyExecutor])
+class PolicyExecutorSpec extends Specification {
+
+ @Autowired
+ PolicyExecutor objectUnderTest
+
+ def logAppender = Spy(ListAppender<ILoggingEvent>)
+
+ def setup() {
+ setupLogger()
+ }
+
+ def cleanup() {
+ ((Logger) LoggerFactory.getLogger(PolicyExecutor)).detachAndStopAllAppenders()
+ }
+
+ def 'Configuration properties.'() {
+ expect: 'properties used from application.yml'
+ assert objectUnderTest.enabled
+ assert objectUnderTest.serverAddress == 'http://localhost'
+ assert objectUnderTest.serverPort == '8785'
+ }
+
+ def 'Permission check logging.'() {
+ when: 'permission is checked for an operation'
+ def yangModelCmHandle = new YangModelCmHandle(id:'ch-1', alternateId:'fdn1')
+ objectUnderTest.checkPermission(yangModelCmHandle, PATCH, 'my credentials','my resource','my change')
+ then: 'correct details are logged '
+ assert getLogEntry(0) == 'Policy Executor Enabled'
+ assert getLogEntry(3).contains('my credentials')
+ assert getLogEntry(4).contains('cm_patch')
+ assert getLogEntry(5).contains('fdn1')
+ assert getLogEntry(6).contains('ch-1')
+ assert getLogEntry(7).contains('my resource')
+ assert getLogEntry(8).contains('my change')
+ }
+
+ def 'Permission check with feature disabled.'() {
+ given: 'feature is disabled'
+ objectUnderTest.enabled = false
+ when: 'permission is checked for an operation'
+ objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource','my change')
+ then: 'nothing is logged'
+ assert logAppender.list.isEmpty()
+ }
+
+ def setupLogger() {
+ def logger = LoggerFactory.getLogger(PolicyExecutor)
+ logger.setLevel(Level.DEBUG)
+ logger.addAppender(logAppender)
+ logAppender.start()
+ }
+
+ def getLogEntry(index) {
+ logAppender.list[index].formattedMessage
+ }
+}
diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml
index 5b10e7376b..f0790dda4b 100644
--- a/cps-ncmp-service/src/test/resources/application.yml
+++ b/cps-ncmp-service/src/test/resources/application.yml
@@ -41,10 +41,12 @@ ncmp:
pendingAcquireMaxCount: 22
connectionTimeoutInSeconds: 123
maximumInMemorySizeInMegabytes: 7
+ readTimeoutInSeconds: 33
model-services:
pendingAcquireMaxCount: 44
connectionTimeoutInSeconds: 456
maximumInMemorySizeInMegabytes: 8
+ maximumConnectionsTotal: 111
auth:
username: some-user
password: some-password
@@ -59,6 +61,11 @@ ncmp:
async-executor:
parallelism-level: 3
+ policy-executor:
+ enabled: true
+ server:
+ address: "http://localhost"
+ port: "8785"
# Custom Hazelcast Config.
hazelcast: