From 32841130d8f9f07af4b66d41e70dddaf2b5b9427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Wed, 17 Dec 2025 10:58:39 +0000 Subject: [PATCH 1/2] Integration with secrets controller for automatic unsealing Implements an optional integration with the secrets controller to transparently unseal secret characteristics. --- .../etsi/osl/osom/lcm/LcmBaseExecutor.java | 13 ++++- .../org/etsi/osl/osom/lcm/SecretsClient.java | 47 +++++++++++++++++++ .../osom/management/ServiceOrderManager.java | 20 ++++---- src/main/resources/application.yml | 6 ++- 4 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/etsi/osl/osom/lcm/SecretsClient.java diff --git a/src/main/java/org/etsi/osl/osom/lcm/LcmBaseExecutor.java b/src/main/java/org/etsi/osl/osom/lcm/LcmBaseExecutor.java index fbe909d..39aea66 100644 --- a/src/main/java/org/etsi/osl/osom/lcm/LcmBaseExecutor.java +++ b/src/main/java/org/etsi/osl/osom/lcm/LcmBaseExecutor.java @@ -150,14 +150,23 @@ public abstract class LcmBaseExecutor { if (c.getName().equals(charName)) { if (c.getValue() != null) { if (c.getValue().getValue() != null) { + // Unseal secrets as needed + if (c.getValueType().equals(EValueType.SECRET.getValue())) { + final var secretsClient = this.vars.getServiceOrderManager().getSecretsClient(); + if (secretsClient.isPresent()) { + c.getValue().setValue( + secretsClient.get().unseal(c.getValue().getValue(), c.getName())); + } + } + return Optional.of(c); } } } } } - Characteristic z = null; - return Optional.ofNullable(z); + + return Optional.empty(); } public void setCharValFromStringType(String charName, String newValue) { diff --git a/src/main/java/org/etsi/osl/osom/lcm/SecretsClient.java b/src/main/java/org/etsi/osl/osom/lcm/SecretsClient.java new file mode 100644 index 0000000..96b2a4d --- /dev/null +++ b/src/main/java/org/etsi/osl/osom/lcm/SecretsClient.java @@ -0,0 +1,47 @@ +package org.etsi.osl.osom.lcm; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.camel.ProducerTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +@Service +@ConditionalOnExpression("${SECRETS_CONTROLLER_ENABLE:false}") +public class SecretsClient { + private final ProducerTemplate producerTemplate; + + private final String unsealSecretQueue; + + private final ObjectMapper mapper = new ObjectMapper(); + + @Autowired + public SecretsClient( + ProducerTemplate producerTemplate, + @Value("${SECRETS_CHARACTERISTIC_UNSEAL}") String unsealSecretQueue + ) { + this.producerTemplate = producerTemplate; + this.unsealSecretQueue = unsealSecretQueue; + } + + public String unseal(String uri, String characteristic) { + try { + final var cmd = new UnsealCharacteristicCommand(uri, characteristic); + final var payload = this.mapper.writeValueAsString(cmd); + final var responseRaw = this.producerTemplate.requestBody(this.unsealSecretQueue, payload, String.class); + final var response = this.mapper.readValue(responseRaw, String.class); + + if (response == null) + return ""; + + return response; + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + private record UnsealCharacteristicCommand(String secretURI, String characteristic) { + } +} diff --git a/src/main/java/org/etsi/osl/osom/management/ServiceOrderManager.java b/src/main/java/org/etsi/osl/osom/management/ServiceOrderManager.java index 91729c3..9bcb0d5 100644 --- a/src/main/java/org/etsi/osl/osom/management/ServiceOrderManager.java +++ b/src/main/java/org/etsi/osl/osom/management/ServiceOrderManager.java @@ -20,11 +20,7 @@ package org.etsi.osl.osom.management; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; @@ -35,6 +31,7 @@ import org.etsi.osl.model.nfv.DeploymentDescriptor; import org.etsi.osl.model.nfv.NetworkServiceDescriptor; import org.etsi.osl.model.nfv.ScaleDescriptor; import org.etsi.osl.osom.lcm.ChatClient; +import org.etsi.osl.osom.lcm.SecretsClient; import org.etsi.osl.osom.serviceactions.NSActionRequestPayload; import org.etsi.osl.tmf.pm628.model.MeasurementCollectionJob; import org.etsi.osl.tmf.pm628.model.MeasurementCollectionJobFVO; @@ -208,7 +205,9 @@ public class ServiceOrderManager { @Autowired private ChatClient chatClient; - + @Autowired + private Optional secretsClient; + @Transactional public void processOrder(ServiceOrder serviceOrder) { @@ -1209,12 +1208,11 @@ public class ServiceOrderManager { } public ChatClient getChatClient(String agentName) { - + return chatClient; } - - - - + public Optional getSecretsClient() { + return this.secretsClient; + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index baeda01..ee80945 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -128,4 +128,8 @@ CRD_PATCH_CR_REQ: "jms:queue:CRD.PATCH.CR_REQ" #TMF628 ACTIONS PM_MEASUREMENT_COLLECTION_JOB_ADD: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.ADD" -PM_MEASUREMENT_COLLECTION_JOB_UPDATE: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.UPDATE" \ No newline at end of file +PM_MEASUREMENT_COLLECTION_JOB_UPDATE: "jms:queue:PM.MEASUREMENTCOLLECTIONJOB.UPDATE" + +# Secrets controller ACTIONS +SECRETS_CONTROLLER_ENABLE: false +SECRETS_CHARACTERISTIC_UNSEAL: "jms:queue:SECRETS.CHARACTERISTIC.UNSEAL?exchangePattern=InOut" -- GitLab From e74e40781f576da9e70d035bb86ad186ae4bc65a Mon Sep 17 00:00:00 2001 From: Kostis Trantzas Date: Fri, 17 Jul 2026 15:29:25 +0300 Subject: [PATCH 2/2] Reinforce error handling of secrets client --- .../org/etsi/osl/osom/lcm/SecretsClient.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/etsi/osl/osom/lcm/SecretsClient.java b/src/main/java/org/etsi/osl/osom/lcm/SecretsClient.java index 96b2a4d..6912c7d 100644 --- a/src/main/java/org/etsi/osl/osom/lcm/SecretsClient.java +++ b/src/main/java/org/etsi/osl/osom/lcm/SecretsClient.java @@ -7,10 +7,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + @Service @ConditionalOnExpression("${SECRETS_CONTROLLER_ENABLE:false}") public class SecretsClient { + + private static final transient Log logger = LogFactory.getLog( SecretsClient.class.getName() ); + private final ProducerTemplate producerTemplate; private final String unsealSecretQueue; @@ -31,14 +37,24 @@ public class SecretsClient { final var cmd = new UnsealCharacteristicCommand(uri, characteristic); final var payload = this.mapper.writeValueAsString(cmd); final var responseRaw = this.producerTemplate.requestBody(this.unsealSecretQueue, payload, String.class); + + if (responseRaw == null || responseRaw.isEmpty()) { + return uri; + } + final var response = this.mapper.readValue(responseRaw, String.class); - if (response == null) - return ""; + if (response == null || response.isEmpty()) { + return uri; + } return response; } catch (JsonProcessingException e) { throw new RuntimeException(e); + } catch (Exception e) { + // controller is enabled by the request failed (no route, timeout, broker down) + logger.warn("Secrets controller did not respond for " + uri, e); + return uri; } } -- GitLab