2 * Copyright © 2020 Bell Canada
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api
19 import com.fasterxml.jackson.databind.JsonNode
20 import com.fasterxml.jackson.databind.node.ObjectNode
21 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
22 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceOutput
23 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants
24 import org.onap.ccsdk.cds.blueprintsprocessor.message.service.BluePrintMessageLibPropertyService
25 import org.onap.ccsdk.cds.blueprintsprocessor.message.service.BlueprintMessageProducerService
26 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
27 import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
28 import org.onap.ccsdk.cds.controllerblueprints.core.common.ApplicationConstants
29 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
30 import org.onap.ccsdk.cds.controllerblueprints.core.service.PropertyAssignmentService
31 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
32 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
33 import org.onap.ccsdk.cds.controllerblueprints.core.utils.PropertyDefinitionUtils
34 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
35 import org.slf4j.LoggerFactory
36 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
37 import org.springframework.stereotype.Service
38 import javax.annotation.PostConstruct
41 * Audit service used to produce execution service input and output message
42 * sent into dedicated kafka topics.
44 @ConditionalOnProperty(
45 name = ["blueprintsprocessor.messageproducer.self-service-api.audit.kafkaEnable"],
49 class KafkaPublishAuditService(
50 private val bluePrintMessageLibPropertyService: BluePrintMessageLibPropertyService,
51 private val blueprintsProcessorCatalogService: BluePrintCatalogService
52 ) : PublishAuditService {
53 private var inputInstance: BlueprintMessageProducerService? = null
54 private var outputInstance: BlueprintMessageProducerService? = null
55 private val log = LoggerFactory.getLogger(KafkaPublishAuditService::class.toString())
58 const val INPUT_SELECTOR = "self-service-api.audit.request"
59 const val OUTPUT_SELECTOR = "self-service-api.audit.response"
64 log.info("Kakfa audit service is enabled")
68 * Publish execution input into a kafka topic.
69 * The correlation UUID is used to link the input to its output.
70 * Sensitive data within the request are hidden.
72 override suspend fun publishExecutionInput(executionServiceInput: ExecutionServiceInput) {
73 val secureExecutionServiceInput = hideSensitiveData(executionServiceInput)
75 this.inputInstance = this.getInputInstance(INPUT_SELECTOR)
76 this.inputInstance!!.sendMessage(secureExecutionServiceInput)
77 } catch (e: Exception) {
79 if (e.message != null) "ERROR : ${e.message}"
80 else "ERROR : Failed to send execution request to Kafka."
86 * Publish execution output into a kafka topic.
87 * The correlation UUID is used to link the output to its input.
88 * A correlation UUID is added to link the input to its output.
90 override suspend fun publishExecutionOutput(correlationUUID: String, executionServiceOutput: ExecutionServiceOutput) {
91 executionServiceOutput.correlationUUID = correlationUUID
93 this.outputInstance = this.getOutputInstance(OUTPUT_SELECTOR)
94 this.outputInstance!!.sendMessage(executionServiceOutput)
95 } catch (e: Exception) {
97 if (e.message != null) "ERROR : $e"
98 else "ERROR : Failed to send execution request to Kafka."
104 * Return the input kafka producer instance using a selector.
106 private fun getInputInstance(selector: String): BlueprintMessageProducerService = inputInstance ?: createInstance(selector)
109 * Return the output kafka producer instance using a selector.
111 private fun getOutputInstance(selector: String): BlueprintMessageProducerService = outputInstance ?: createInstance(selector)
114 * Create a kafka producer instance.
116 private fun createInstance(selector: String): BlueprintMessageProducerService {
117 log.info("Setting up message producer($selector)...")
118 return bluePrintMessageLibPropertyService.blueprintMessageProducerService(selector)
122 * Hide sensitive data in the request.
123 * Sensitive data are declared in the resource resolution mapping using
124 * the property metadata "log-protect" set to true.
126 private suspend fun hideSensitiveData(
127 executionServiceInput: ExecutionServiceInput
128 ): ExecutionServiceInput {
130 var clonedExecutionServiceInput = ExecutionServiceInput().apply {
131 correlationUUID = executionServiceInput.correlationUUID
132 commonHeader = executionServiceInput.commonHeader
133 actionIdentifiers = executionServiceInput.actionIdentifiers
134 payload = executionServiceInput.payload.deepCopy()
135 stepData = executionServiceInput.stepData
138 val blueprintName = clonedExecutionServiceInput.actionIdentifiers.blueprintName
139 val workflowName = clonedExecutionServiceInput.actionIdentifiers.actionName
141 if (blueprintName == "default") return clonedExecutionServiceInput
144 if (clonedExecutionServiceInput.payload
145 .path("$workflowName-request").has("$workflowName-properties")) {
147 /** Retrieving sensitive input parameters */
148 val requestId = clonedExecutionServiceInput.commonHeader.requestId
149 val blueprintVersion = clonedExecutionServiceInput.actionIdentifiers.blueprintVersion
151 val basePath = blueprintsProcessorCatalogService.getFromDatabase(blueprintName, blueprintVersion)
153 val blueprintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime(requestId, basePath.toString())
154 val blueprintContext = blueprintRuntimeService.bluePrintContext()
156 /** Looking for node templates defined as component-resource-resolution */
157 val nodeTemplates = blueprintContext.nodeTemplates()
158 nodeTemplates!!.forEach { nodeTemplate ->
159 val nodeTemplateName = nodeTemplate.key
160 val nodeTemplateType = blueprintContext.nodeTemplateByName(nodeTemplateName).type
161 if (nodeTemplateType == BluePrintConstants.NODE_TEMPLATE_TYPE_COMPONENT_RESOURCE_RESOLUTION) {
162 val interfaceName = blueprintContext.nodeTemplateFirstInterfaceName(nodeTemplateName)
163 val operationName = blueprintContext.nodeTemplateFirstInterfaceFirstOperationName(nodeTemplateName)
165 val propertyAssignments: MutableMap<String, JsonNode> =
166 blueprintContext.nodeTemplateInterfaceOperationInputs(nodeTemplateName, interfaceName, operationName)
169 /** Getting values define in artifact-prefix-names */
170 val input = executionServiceInput.payload.get("$workflowName-request")
171 blueprintRuntimeService.assignWorkflowInputs(workflowName, input)
172 val artifactPrefixNamesNode = propertyAssignments[ResourceResolutionConstants.INPUT_ARTIFACT_PREFIX_NAMES]
173 val propertyAssignmentService = PropertyAssignmentService(blueprintRuntimeService)
174 val artifactPrefixNamesNodeValue = propertyAssignmentService.resolveAssignmentExpression(
175 BluePrintConstants.MODEL_DEFINITION_TYPE_NODE_TEMPLATE,
177 ResourceResolutionConstants.INPUT_ARTIFACT_PREFIX_NAMES,
178 artifactPrefixNamesNode!!)
180 val artifactPrefixNames = JacksonUtils.getListFromJsonNode(artifactPrefixNamesNodeValue!!, String::class.java)
182 /** Storing mapping entries with metadata log-protect set to true */
183 val sensitiveParameters: List<String> = artifactPrefixNames
184 .map { "$it-mapping" }
185 .map { blueprintRuntimeService.resolveNodeTemplateArtifact(nodeTemplateName, it) }
186 .flatMap { JacksonUtils.getListFromJson(it, ResourceAssignment::class.java) }
187 .filter { PropertyDefinitionUtils.hasLogProtect(it.property) }
190 /** Hiding sensitive input parameters from the request */
191 var workflowProperties: ObjectNode = clonedExecutionServiceInput.payload
192 .path("$workflowName-request")
193 .path("$workflowName-properties") as ObjectNode
195 sensitiveParameters.forEach { sensitiveParameter ->
196 if (workflowProperties.has(sensitiveParameter)) {
197 workflowProperties.replace(sensitiveParameter, ApplicationConstants.LOG_REDACTED.asJsonPrimitive())
203 } catch (e: Exception) {
204 val errMsg = "ERROR : Couldn't hide sensitive data in the execution request."
206 clonedExecutionServiceInput.payload.replace(
207 "$workflowName-request",
208 "$errMsg $e".asJsonPrimitive())
210 return clonedExecutionServiceInput