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.BluePrintProcessorException
27 import org.onap.ccsdk.cds.controllerblueprints.core.common.ApplicationConstants
28 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
29 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
30 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
31 import org.onap.ccsdk.cds.controllerblueprints.core.utils.PropertyDefinitionUtils
32 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
33 import org.slf4j.LoggerFactory
34 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
35 import org.springframework.stereotype.Service
36 import javax.annotation.PostConstruct
39 * Audit service used to produce execution service input and output message
40 * sent into dedicated kafka topics.
42 @ConditionalOnProperty(
43 name = ["blueprintsprocessor.messageproducer.self-service-api.audit.kafkaEnable"],
47 class KafkaPublishAuditService(
48 private val bluePrintMessageLibPropertyService: BluePrintMessageLibPropertyService,
49 private val blueprintsProcessorCatalogService: BluePrintCatalogService
50 ) : PublishAuditService {
52 private var inputInstance: BlueprintMessageProducerService? = null
53 private var outputInstance: BlueprintMessageProducerService? = null
55 private lateinit var correlationUUID: String
57 private val log = LoggerFactory.getLogger(KafkaPublishAuditService::class.toString())
60 const val INPUT_SELECTOR = "self-service-api.audit.request"
61 const val OUTPUT_SELECTOR = "self-service-api.audit.response"
66 log.info("Kakfa audit service is enabled")
70 * Publish execution input into a kafka topic.
71 * The correlation UUID is used to link the input to its output.
72 * Sensitive data within the request are hidden.
74 override suspend fun publish(executionServiceInput: ExecutionServiceInput) {
75 this.correlationUUID = executionServiceInput.correlationUUID
76 val secureExecutionServiceInput = hideSensitiveData(executionServiceInput)
77 this.inputInstance = this.getInputInstance(INPUT_SELECTOR)
78 this.inputInstance!!.sendMessage(secureExecutionServiceInput)
82 * Publish execution output into a kafka topic.
83 * The correlation UUID is used to link the output to its input.
84 * A correlation UUID is added to link the input to its output.
86 override fun publish(executionServiceOutput: ExecutionServiceOutput) {
87 executionServiceOutput.correlationUUID = this.correlationUUID
88 this.outputInstance = this.getOutputInstance(OUTPUT_SELECTOR)
89 this.outputInstance!!.sendMessage(executionServiceOutput)
93 * Return the input kafka producer instance using a selector.
95 private fun getInputInstance(selector: String): BlueprintMessageProducerService = inputInstance ?: createInstance(selector)
98 * Return the output kafka producer instance using a selector.
100 private fun getOutputInstance(selector: String): BlueprintMessageProducerService = outputInstance ?: createInstance(selector)
103 * Create a kafka producer instance.
105 private fun createInstance(selector: String): BlueprintMessageProducerService {
107 "Setting up message producer($selector)..."
110 bluePrintMessageLibPropertyService
111 .blueprintMessageProducerService(selector)
112 } catch (e: Exception) {
113 throw BluePrintProcessorException("failed to create producer service ${e.message}")
118 * Hide sensitive data in the request.
119 * Sensitive data are declared in the resource resolution mapping using
120 * the property metadata "log-protect" set to true.
122 private suspend fun hideSensitiveData(
123 executionServiceInput: ExecutionServiceInput
124 ): ExecutionServiceInput {
126 var clonedExecutionServiceInput = ExecutionServiceInput().apply {
127 correlationUUID = executionServiceInput.correlationUUID
128 commonHeader = executionServiceInput.commonHeader
129 actionIdentifiers = executionServiceInput.actionIdentifiers
130 payload = executionServiceInput.payload
133 val blueprintName = clonedExecutionServiceInput.actionIdentifiers.blueprintName
134 val workflowName = clonedExecutionServiceInput.actionIdentifiers.actionName
136 if (blueprintName == "default") return clonedExecutionServiceInput
138 if (clonedExecutionServiceInput.payload
139 .path("$workflowName-request").has("$workflowName-properties")) {
141 /** Retrieving sensitive input parameters */
142 val requestId = clonedExecutionServiceInput.commonHeader.requestId
143 val blueprintVersion = clonedExecutionServiceInput.actionIdentifiers.blueprintVersion
145 val basePath = blueprintsProcessorCatalogService.getFromDatabase(blueprintName, blueprintVersion)
147 val blueprintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime(requestId, basePath.toString())
148 val blueprintContext = blueprintRuntimeService.bluePrintContext()
150 val nodeTemplateName = blueprintContext.workflowFirstStepNodeTemplate(workflowName)
151 val interfaceName = blueprintContext.nodeTemplateFirstInterfaceName(nodeTemplateName)
152 val operationName = blueprintContext.nodeTemplateFirstInterfaceFirstOperationName(nodeTemplateName)
154 val propertyAssignments: MutableMap<String, JsonNode> =
155 blueprintContext.nodeTemplateInterfaceOperationInputs(nodeTemplateName, interfaceName, operationName)
158 val artifactPrefixNamesNode = propertyAssignments[ResourceResolutionConstants.INPUT_ARTIFACT_PREFIX_NAMES]
159 val artifactPrefixNames = JacksonUtils.getListFromJsonNode(artifactPrefixNamesNode!!, String::class.java)
161 /** Storing mapping entries with metadata log-protect set to true */
162 val sensitiveParameters: List<String> = artifactPrefixNames
163 .map { "$it-mapping" }
164 .map { blueprintRuntimeService.resolveNodeTemplateArtifact(nodeTemplateName, it) }
165 .flatMap { JacksonUtils.getListFromJson(it, ResourceAssignment::class.java) }
166 .filter { PropertyDefinitionUtils.hasLogProtect(it.property) }
169 /** Hiding sensitive input parameters from the request */
170 var workflowProperties: ObjectNode = clonedExecutionServiceInput.payload
171 .path("$workflowName-request")
172 .path("$workflowName-properties") as ObjectNode
174 sensitiveParameters.forEach { sensitiveParameter ->
175 if (workflowProperties.has(sensitiveParameter)) {
176 workflowProperties.remove(sensitiveParameter)
177 workflowProperties.put(sensitiveParameter, ApplicationConstants.LOG_REDACTED)
182 return clonedExecutionServiceInput