Verified Commit dcb3f3f0 authored by João Capucho's avatar João Capucho
Browse files

Implement resource secret support

parent ee7b2a0b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -68,6 +68,10 @@
            <artifactId>org.etsi.osl.model.tmf</artifactId>
            <version>${org.etsi.osl.model.tmf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <build>
+14 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@ public class CatalogClient {
    @Value("${topics.CATALOG_UPD_SERVICE}")
    private String CATALOG_UPD_SERVICE;

    @Value("${topics.CATALOG_UPD_RESOURCE}")
    private String CATALOG_UPD_RESOURCE;

    @Autowired
    public CatalogClient(JmsClient jmsClient, ObjectMapper objectMapper) {
        this.jmsClient = jmsClient;
@@ -32,4 +35,15 @@ public class CatalogClient {
        jmsClient.destination(CATALOG_UPD_SERVICE)
                .send(payload, map);
    }

    public void updateResource(String resourceId, org.etsi.osl.tmf.ri639.model.ResourceUpdate resourceUpdate) {
        final var map = new HashMap<String, Object>();
        map.put("resourceId", resourceId);
        map.put("triggerServiceActionQueue", false);

        final var payload = this.objectMapper.writeValueAsString(resourceUpdate);

        jmsClient.destination(CATALOG_UPD_RESOURCE)
                .send(payload, map);
    }
}
+114 −0
Original line number Diff line number Diff line
package org.etsi.osl.secretcontroller;

import org.etsi.osl.secretcontroller.config.RulesConfig;
import org.etsi.osl.secretcontroller.rules.CharacteristicRule;
import org.etsi.osl.secretcontroller.rules.ResourceRule;
import org.etsi.osl.secretcontroller.rules.RuleEvaluatorService;
import org.etsi.osl.tmf.ri639.model.Characteristic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tools.jackson.core.exc.StreamReadException;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Service
public class ResourceRulesService {
    private static final Logger log = LoggerFactory.getLogger(ResourceRulesService.class);

    private final RulesConfig rulesConfig;
    private final RuleEvaluatorService ruleEvaluatorService;
    private final ObjectMapper mapper;

    @Autowired
    public ResourceRulesService(RulesConfig rulesConfig, RuleEvaluatorService ruleEvaluatorService,
                                ObjectMapper mapper) {
        this.rulesConfig = rulesConfig;
        this.ruleEvaluatorService = ruleEvaluatorService;
        this.mapper = mapper;
    }

    public Optional<ResourceRulesApplier> getResourceRules(org.etsi.osl.tmf.ri639.model.Resource resource) {
        final var cat = resource.getCategory();
        final var rule = this.rulesConfig.getResourceRules().get(cat);

        if (rule == null)
            return Optional.empty();

        return Optional.of(new ResourceRulesApplier(this.mapper, this.ruleEvaluatorService, resource, rule));
    }

    public static class ResourceRulesApplier {
        private final ResourceRule rule;
        private final RuleEvaluatorService ruleEvaluatorService;

        private final Map<String, CharacteristicRule> extraRules = new HashMap<>();

        public ResourceRulesApplier(ObjectMapper mapper,
                                    RuleEvaluatorService ruleEvaluatorService,
                                    org.etsi.osl.tmf.ri639.model.Resource resource,
                                    ResourceRule rule) {
            this.rule = rule;
            this.ruleEvaluatorService = ruleEvaluatorService;

            if (!rule.isKubernetesSecret())
                return;

            // Kubernetes secret extra handling
            final var rawSecret = resource.getResourceCharacteristicByName("json").getValue().getValue();
            final JsonNode json;
            try {
                json = mapper.readTree(rawSecret);
            } catch (StreamReadException e) {
                // If reading fails the secret most likely has already been erased
                log.debug(e.getMessage());
                return;
            }

            final var secretData = json.get("data");
            // The secret may have already been erased
            if (secretData == null || !secretData.isObject())
                return;

            final var replaceRule = new CharacteristicRule();
            replaceRule.setAction(SecretApplyResult.Action.Replace);
            replaceRule.setBase64Decode(true);

            // Convert all characteristics with secret data into secrets
            for (final var field : secretData.propertyNames())
                this.extraRules.put(field, replaceRule);

            final var eraseRule = new CharacteristicRule();
            eraseRule.setAction(SecretApplyResult.Action.Erase);

            // All these fields contain the secret data in one way or another and must be erased
            this.extraRules.put("data", eraseRule);
            this.extraRules.put("json", eraseRule);
            this.extraRules.put("metadata", eraseRule);
        }

        public SecretApplyResult saveCharacteristic(Characteristic characteristic) {
            final var name = characteristic.getName();

            final var extraRule = this.extraRules.get(name);
            if (extraRule != null)
                return this.ruleEvaluatorService.evaluateRule(characteristic.getValue().getValue(), extraRule);

            for (final var cRule : this.rule.getCharacteristics()) {
                final var m = cRule.getMatch().matcher(name);
                if (!m.matches())
                    continue;

                final var value = characteristic.getValue().getValue();
                return this.ruleEvaluatorService.evaluateRule(value, cRule);
            }

            return SecretApplyResult.ignore();
        }
    }
}
+35 −0
Original line number Diff line number Diff line
package org.etsi.osl.secretcontroller;

public class SecretApplyResult {
    public final Action action;
    private final String value;

    private SecretApplyResult(Action action, String value) {
        this.action = action;
        this.value = value;
    }

    public static SecretApplyResult replace(String value) {
        return new SecretApplyResult(Action.Replace, value);
    }

    public static SecretApplyResult erase() {
        return new SecretApplyResult(Action.Erase, null);
    }

    public static SecretApplyResult ignore() {
        return new SecretApplyResult(Action.Ignore, null);
    }

    public String getValue() {
        if (this.action != Action.Replace)
            throw new IllegalStateException("Value is only available when replacing");
        return this.value;
    }

    public enum Action {
        Replace,
        Erase,
        Ignore,
    }
}
+57 −0
Original line number Diff line number Diff line
package org.etsi.osl.secretcontroller;

import org.etsi.osl.secretcontroller.config.RulesConfig;
import org.etsi.osl.secretcontroller.rules.RuleEvaluatorService;
import org.etsi.osl.secretcontroller.rules.ServiceRule;
import org.etsi.osl.tmf.common.model.service.Characteristic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class ServiceRulesService {
    private final RulesConfig rulesConfig;
    private final RuleEvaluatorService ruleEvaluatorService;

    @Autowired
    public ServiceRulesService(RulesConfig rulesConfig, RuleEvaluatorService ruleEvaluatorService) {
        this.rulesConfig = rulesConfig;
        this.ruleEvaluatorService = ruleEvaluatorService;
    }

    public Optional<ServiceRulesApplier> getServiceRules(org.etsi.osl.tmf.sim638.model.Service svc) {
        final var name = svc.getName();
        final var rule = this.rulesConfig.getServiceRules().get(name);

        if (rule == null)
            return Optional.empty();

        return Optional.of(new ServiceRulesApplier(this.ruleEvaluatorService, rule));
    }

    public static class ServiceRulesApplier {
        private final ServiceRule rule;
        private final RuleEvaluatorService ruleEvaluatorService;

        public ServiceRulesApplier(RuleEvaluatorService ruleEvaluatorService, ServiceRule rule) {
            this.rule = rule;
            this.ruleEvaluatorService = ruleEvaluatorService;
        }

        public SecretApplyResult saveCharacteristic(Characteristic characteristic) {
            final var name = characteristic.getName();

            for (final var cRule : this.rule.characteristics()) {
                final var m = cRule.getMatch().matcher(name);
                if (!m.matches())
                    continue;

                var value = characteristic.getValue().getValue();
                return this.ruleEvaluatorService.evaluateRule(value, cRule);
            }

            return SecretApplyResult.ignore();
        }
    }
}
Loading