Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ steps:
OPENAEV_ADMIN_EMAIL: [email protected]
OPENAEV_ADMIN_PASSWORD: admin
OPENAEV_ADMIN_TOKEN: 0d17ce9a-f3a8-4c6d-9721-c98dc3dc023f
OPENAEV_ADMIN_ENCRYPTION_KEY: ThisIsMyUltraSecureEncryptionKey
OPENAEV_ADMIN_ENCRYPTION_SALT: ilikesaltyfoodnomnom
SPRING_PROFILES_ACTIVE: ci
OPENBAS_RABBITMQ_HOSTNAME: rabbitmq-e2e
commands:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.openaev.database.model.ConnectorInstancePersisted;
import io.openaev.helper.ConnectorInstanceHashHelper;
import io.openaev.service.connector_instances.ConnectorInstanceService;
import io.openaev.service.connector_instances.EncryptionService;
import io.openaev.utils.reflection.FieldUtils;
import java.lang.reflect.Field;
import java.util.List;
Expand All @@ -17,6 +18,7 @@ public abstract class Integration {
private final ComponentRequestEngine componentRequestEngine;
@Getter private ConnectorInstance connectorInstance;
private final ConnectorInstanceService connectorInstanceService;
protected EncryptionService encryptionService;

@Getter
protected ConnectorInstance.CURRENT_STATUS_TYPE currentStatus =
Expand All @@ -27,16 +29,21 @@ public abstract class Integration {
protected Integration(
ComponentRequestEngine componentRequestEngine,
ConnectorInstance connectorInstance,
ConnectorInstanceService connectorInstanceService) {
ConnectorInstanceService connectorInstanceService,
EncryptionService encryptionService) {
this.componentRequestEngine = componentRequestEngine;
this.connectorInstance = connectorInstance;
this.connectorInstanceService = connectorInstanceService;
this.encryptionService = encryptionService;
}

protected abstract void innerStart() throws Exception;

protected abstract void refresh() throws Exception;

private void start() throws Exception {
if (ConnectorInstancePersisted.CURRENT_STATUS_TYPE.stopped.equals(this.currentStatus)) {
this.refresh();
this.innerStart();
this.currentStatus = ConnectorInstance.CURRENT_STATUS_TYPE.started;
this.appliedHash = ConnectorInstanceHashHelper.computeInstanceHash(this.connectorInstance);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package io.openaev.integration;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.openaev.authorisation.HttpClientFactory;
import io.openaev.database.model.ConnectorInstance;
import io.openaev.service.catalog_connectors.CatalogConnectorService;
import io.openaev.service.connector_instances.ConnectorInstanceService;
import io.openaev.service.connector_instances.EncryptionFactory;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -12,8 +14,10 @@

@RequiredArgsConstructor
public abstract class IntegrationFactory {
private final ConnectorInstanceService connectorInstanceService;
private final CatalogConnectorService catalogConnectorService;
protected final ConnectorInstanceService connectorInstanceService;
protected final CatalogConnectorService catalogConnectorService;
protected final EncryptionFactory encryptionFactory;
protected final HttpClientFactory httpClientFactory;

protected abstract void runMigrations() throws Exception;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import static io.openaev.database.model.CatalogConnectorConfiguration.ENCRYPTED_FORMATS;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.openaev.database.model.CatalogConnector;
import io.openaev.database.model.CatalogConnectorConfiguration;
import io.openaev.database.model.ConnectorInstanceConfiguration;
import io.openaev.database.model.ConnectorInstancePersisted;
import io.openaev.database.model.*;
import io.openaev.rest.exception.UnencryptableElementException;
import io.openaev.service.connector_instances.EncryptionService;
import io.openaev.utils.JsonUtils;
import io.openaev.utils.reflection.FieldUtils;
import jakarta.validation.constraints.NotNull;
Expand All @@ -20,13 +20,17 @@
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BaseIntegrationConfiguration {
private final ObjectMapper mapper = new ObjectMapper();
@Getter @Setter private boolean enable = false;

public static <T extends BaseIntegrationConfiguration> T fromConnectorInstanceConfigurationSet(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method feels overly complex to me. Could we simplify it by passing the config and only decrypting the relevant keys?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method do not only do decrypting. It's used to create a Configuration java model from the database model. I guess I can simplify it a bit with another private method but it can't be used only for the relevant keys.

@NotNull Set<ConnectorInstanceConfiguration> configurations, Class<T> targetClass)
@NotNull ConnectorInstance instance,
Class<T> targetClass,
EncryptionService encryptionService)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels odd to pass a service as a method argument.
This breaks the dependency injection model.
We should move this logic into a dedicated service and embed the method there instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I do agree that was my initial intention but I had cyclic dependencies. I did not think of moving part of the code to a dedicated utils, I'll try to change that :)

throws NoSuchMethodException,
InvocationTargetException,
InstantiationException,
Expand All @@ -37,12 +41,25 @@ public static <T extends BaseIntegrationConfiguration> T fromConnectorInstanceCo
FieldUtils.getAllDeclaredAnnotatedFields(targetClass, IntegrationConfigKey.class);
for (Field field : annotatedFields) {
Optional<ConnectorInstanceConfiguration> config =
configurations.stream()
instance.getConfigurations().stream()
.filter(c -> c.getKey().equals(field.getAnnotation(IntegrationConfigKey.class).key()))
.findFirst();
if (config.isPresent()) {
FieldUtils.setField(
newObj, field, JsonUtils.fromJsonNode(config.get().getValue(), field.getType()));
// If the field is encrypted and can be decrypted
if (config.get().isEncrypted() && encryptionService != null) {
// Decrypt the field and set it
FieldUtils.setField(
newObj,
field,
JsonUtils.fromJsonNode(
new ObjectMapper()
.valueToTree(encryptionService.decrypt(config.get().getValue().asText())),
field.getType()));
} else {
// Otherwise, we just set the field
FieldUtils.setField(
newObj, field, JsonUtils.fromJsonNode(config.get().getValue(), field.getType()));
}
} else {
FieldUtils.setField(newObj, field, null);
}
Expand All @@ -51,20 +68,41 @@ public static <T extends BaseIntegrationConfiguration> T fromConnectorInstanceCo
}

public Set<ConnectorInstanceConfiguration> toInstanceConfigurationSet(
ConnectorInstancePersisted relatedInstance) {
ConnectorInstancePersisted relatedInstance, EncryptionService encryptionService) {
List<Field> annotatedFields =
FieldUtils.getAllDeclaredAnnotatedFields(this.getClass(), IntegrationConfigKey.class);
return annotatedFields.stream()
.map(
af ->
ConnectorInstanceConfiguration.builder()
.key(af.getAnnotation(IntegrationConfigKey.class).key())
.value(mapper.valueToTree(FieldUtils.getField(this, af)))
.isEncrypted(
ENCRYPTED_FORMATS.contains(
af.getAnnotation(IntegrationConfigKey.class).valueFormat()))
.connectorInstance(relatedInstance)
.build())
af -> {
JsonNode value = mapper.valueToTree(FieldUtils.getField(this, af));
boolean isEncrypted =
ENCRYPTED_FORMATS.contains(
af.getAnnotation(IntegrationConfigKey.class).valueFormat());
// If the field is encrypted
if (isEncrypted) {
// If the encryption service is not null, we use it
if (encryptionService != null) {
try {
value = mapper.valueToTree(encryptionService.encrypt(value.toString()));
} catch (Exception e) {
throw new UnencryptableElementException(
"Cannot encrypt the element : " + af.getName(), e);
}
} else {
// If the encryption service is null, there might be an issue with how the
// executor has been initialized
log.warn(
"A encrypted element cannot be decrypted due to the encryption service being null. You might want to look into that as this can cause issue.");
}
}

return ConnectorInstanceConfiguration.builder()
.key(af.getAnnotation(IntegrationConfigKey.class).key())
.value(value)
.isEncrypted(isEncrypted)
.connectorInstance(relatedInstance)
.build();
})
.collect(Collectors.toSet());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
package io.openaev.integration.impl.executors.caldera;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.openaev.authorisation.HttpClientFactory;
import io.openaev.database.model.*;
import io.openaev.executors.ExecutorService;
import io.openaev.executors.caldera.client.CalderaExecutorClient;
import io.openaev.executors.caldera.config.CalderaExecutorConfig;
import io.openaev.executors.caldera.service.CalderaExecutorContextService;
import io.openaev.executors.caldera.service.CalderaExecutorService;
import io.openaev.executors.exception.ExecutorException;
import io.openaev.integration.ComponentRequestEngine;
import io.openaev.integration.Integration;
import io.openaev.integration.QualifiedComponent;
import io.openaev.integration.configuration.BaseIntegrationConfiguration;
import io.openaev.integrations.InjectorService;
import io.openaev.service.AgentService;
import io.openaev.service.EndpointService;
import io.openaev.service.PlatformSettingsService;
import io.openaev.service.connector_instances.ConnectorInstanceService;
import io.openaev.service.connector_instances.EncryptionService;
import java.lang.reflect.InvocationTargetException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Slf4j
public class CalderaExecutorIntegration extends Integration {
public static final String CALDERA_EXECUTOR_DEFAULT_ID = "20696a66-5780-4cbe-b5c1-be43efddb3f7";
public static final String CALDERA_EXECUTOR_TYPE = "openaev_caldera";
Expand All @@ -31,38 +39,47 @@ public class CalderaExecutorIntegration extends Integration {

private CalderaExecutorService calderaExecutorService;

private final CalderaExecutorConfig config;
private final CalderaExecutorClient client;
private CalderaExecutorConfig config;
private CalderaExecutorClient client;
private final AgentService agentService;
private final EndpointService endpointService;
private final InjectorService injectorService;
private final PlatformSettingsService platformSettingsService;
private final ExecutorService executorService;
private final ThreadPoolTaskScheduler taskScheduler;
private final HttpClientFactory httpClientFactory;

private final List<ScheduledFuture<?>> timers = new ArrayList<>();

public CalderaExecutorIntegration(
ConnectorInstance connectorInstance,
ConnectorInstanceService connectorInstanceService,
CalderaExecutorClient client,
CalderaExecutorConfig config,
EndpointService endpointService,
AgentService agentService,
ExecutorService executorService,
ComponentRequestEngine componentRequestEngine,
PlatformSettingsService platformSettingsService,
InjectorService injectorService,
ThreadPoolTaskScheduler taskScheduler) {
super(componentRequestEngine, connectorInstance, connectorInstanceService);
this.client = client;
this.config = config;
ThreadPoolTaskScheduler taskScheduler,
EncryptionService encryptionService,
HttpClientFactory httpClientFactory) {
super(componentRequestEngine, connectorInstance, connectorInstanceService, encryptionService);
this.endpointService = endpointService;
this.agentService = agentService;
this.platformSettingsService = platformSettingsService;
this.injectorService = injectorService;
this.taskScheduler = taskScheduler;
this.executorService = executorService;
this.httpClientFactory = httpClientFactory;

// Refresh the context to get the config
try {
refresh();
} catch (Exception e) {
log.error("Error during initialization of the Caldera Executor", e);
throw new ExecutorException(
e, "Error during initialization of the Executor", CALDERA_EXECUTOR_NAME);
}
}

@Override
Expand All @@ -82,6 +99,7 @@ protected void innerStart() throws Exception {
Endpoint.PLATFORM_TYPE.MacOS.name()
});

client = new CalderaExecutorClient(config, httpClientFactory);
calderaExecutorContextService =
new CalderaExecutorContextService(config, injectorService, client);
calderaExecutorService =
Expand All @@ -101,6 +119,18 @@ protected void innerStart() throws Exception {
this.taskScheduler.scheduleAtFixedRate(calderaExecutorService, Duration.ofSeconds(60)));
}

@Override
protected void refresh()
throws JsonProcessingException,
InvocationTargetException,
NoSuchMethodException,
InstantiationException,
IllegalAccessException {
this.config =
BaseIntegrationConfiguration.fromConnectorInstanceConfigurationSet(
this.getConnectorInstance(), CalderaExecutorConfig.class, this.encryptionService);
}

@Override
protected void innerStop() {
this.platformSettingsService.cleanMessage(BannerMessage.BANNER_KEYS.CALDERA_UNAVAILABLE);
Expand Down
Loading