Skip to content

Commit 83210e9

Browse files
committed
[backend] feat(multitenancy): adding builtin connectors for new tenants (#3505)
1 parent e2be82f commit 83210e9

14 files changed

Lines changed: 92 additions & 127 deletions

File tree

openaev-api/src/main/java/io/openaev/helper/InjectHelper.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
import io.openaev.execution.ExecutionContext;
1212
import io.openaev.execution.ExecutionContextService;
1313
import jakarta.annotation.Resource;
14+
import jakarta.persistence.EntityManager;
1415
import jakarta.validation.constraints.NotNull;
1516
import java.time.Instant;
1617
import java.util.List;
1718
import java.util.stream.Collectors;
1819
import java.util.stream.Stream;
1920
import lombok.RequiredArgsConstructor;
2021
import org.hibernate.Hibernate;
22+
import org.hibernate.Session;
2123
import org.springframework.stereotype.Component;
2224
import org.springframework.transaction.annotation.Transactional;
2325
import reactor.util.function.Tuple2;
@@ -41,6 +43,7 @@ public class InjectHelper {
4143

4244
private final InjectRepository injectRepository;
4345
private final ExecutionContextService executionContextService;
46+
private final EntityManager entityManager;
4447

4548
/**
4649
* Retrieves the teams targeted by an inject.
@@ -121,6 +124,9 @@ private boolean isBeforeOrEqualsNow(Injection injection) {
121124
* @return list of pending injects scheduled within the threshold
122125
*/
123126
public List<Inject> getAllPendingInjectsWithThresholdMinutes(int thresholdMinutes) {
127+
// Disable tenant filter — called from InjectsExecutionJob which runs cross-tenant
128+
entityManager.unwrap(Session.class).disableFilter("tenantFilter");
129+
124130
return this.injectRepository.findAll(
125131
InjectSpecification.pendingInjectWithThresholdMinutes(thresholdMinutes));
126132
}
@@ -153,6 +159,9 @@ private ExecutableInject toExecutableInject(Inject inject) {
153159
*/
154160
@Transactional
155161
public List<ExecutableInject> getInjectsToRun() {
162+
// Disable tenant filter — called from InjectsExecutionJob which runs cross-tenant
163+
entityManager.unwrap(Session.class).disableFilter("tenantFilter");
164+
156165
// Get injects
157166
List<Inject> injects = this.injectRepository.findAll(InjectSpecification.executable());
158167
Stream<ExecutableInject> executableInjects =

openaev-api/src/main/java/io/openaev/integration/ManagerFactory.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package io.openaev.integration;
22

33
import static io.openaev.aop.lock.LockResourceType.MANAGER_FACTORY;
4+
import static io.openaev.helper.StreamHelper.fromIterable;
45

56
import io.openaev.aop.lock.Lock;
67
import io.openaev.context.TenantContext;
8+
import io.openaev.database.audit.TenantAssertionControl;
79
import io.openaev.database.model.Tenant;
10+
import io.openaev.database.repository.TenantRepository;
811
import io.openaev.datapack.DataPackProcessor;
912
import io.openaev.integration.impl.executors.paloaltocortex.PaloAltoCortexExecutorIntegrationFactory;
1013
import io.openaev.integration.impl.executors.sentinelone.SentinelOneExecutorIntegrationFactory;
@@ -15,6 +18,7 @@
1518
import io.openaev.service.PreviewFeatureService;
1619
import io.openaev.service.tenants.UserTenantService;
1720
import jakarta.annotation.PostConstruct;
21+
import jakarta.persistence.EntityManager;
1822
import java.util.List;
1923
import lombok.RequiredArgsConstructor;
2024
import lombok.extern.slf4j.Slf4j;
@@ -27,7 +31,9 @@
2731
public class ManagerFactory implements DependenciesManager {
2832
private final List<IntegrationFactory> factories;
2933
private final List<BuiltinTenantRegistrable> builtinRegistrables;
34+
private final TenantRepository tenantRepository;
3035
private final PreviewFeatureService previewFeatureService;
36+
private final EntityManager entityManager;
3137

3238
@PostConstruct
3339
public void disableFlaggedExecutors() {
@@ -54,6 +60,7 @@ public void disableFlaggedExecutors() {
5460
public Manager getManager() {
5561
if (manager == null) {
5662
try {
63+
registerBuiltinsForAllTenants();
5764
this.manager = new Manager(factories);
5865
this.manager.monitorIntegrations();
5966
} catch (Exception e) {
@@ -63,9 +70,35 @@ public Manager getManager() {
6370
return this.manager;
6471
}
6572

73+
/**
74+
* Ensures built-in connectors are registered for every existing tenant. This covers tenants
75+
* created before the builtin registration mechanism was introduced (e.g. the default tenant
76+
* created by Flyway migration) and is idempotent — safe to run on every startup.
77+
*/
78+
private void registerBuiltinsForAllTenants() {
79+
List<Tenant> tenants = fromIterable(tenantRepository.findAll());
80+
TenantAssertionControl.suppress();
81+
try {
82+
for (Tenant tenant : tenants) {
83+
try {
84+
createDependencyForTenant(tenant);
85+
} catch (DependenciesManagerException e) {
86+
log.error(
87+
"Failed to register built-in connectors for tenant '{}': {}",
88+
tenant.getName(),
89+
e.getMessage(),
90+
e);
91+
}
92+
}
93+
} finally {
94+
TenantAssertionControl.restore();
95+
}
96+
}
97+
6698
// -- TENANT DEPENDENCIES --
6799

68100
@Override
101+
@Transactional
69102
public void createDependencyForTenant(Tenant tenant) throws DependenciesManagerException {
70103

71104
String previousTenant = TenantContext.getCurrentTenant();

openaev-api/src/main/java/io/openaev/integration/impl/executors/openaev/OpenAEVExecutorIntegration.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
package io.openaev.integration.impl.executors.openaev;
22

33
import io.openaev.database.model.ConnectorInstance;
4-
import io.openaev.database.model.Endpoint;
54
import io.openaev.database.repository.AssetAgentJobRepository;
6-
import io.openaev.executors.ExecutorService;
75
import io.openaev.executors.openaev.service.OpenAEVExecutorContextService;
86
import io.openaev.integration.ComponentRequestEngine;
97
import io.openaev.integration.Integration;
108
import io.openaev.integration.QualifiedComponent;
119
import io.openaev.service.connector_instances.ConnectorInstanceService;
1210

1311
public class OpenAEVExecutorIntegration extends Integration {
14-
private final ExecutorService executorService;
1512
private final AssetAgentJobRepository assetAgentJobRepository;
1613

1714
public static final String OPENAEV_EXECUTOR_ID = "2f9a0936-c327-4e95-b406-d161d32a2501";
@@ -27,29 +24,14 @@ public class OpenAEVExecutorIntegration extends Integration {
2724
public OpenAEVExecutorIntegration(
2825
ConnectorInstance connectorInstance,
2926
ConnectorInstanceService connectorInstanceService,
30-
ExecutorService executorService,
3127
AssetAgentJobRepository assetAgentJobRepository,
3228
ComponentRequestEngine componentRequestEngine) {
3329
super(componentRequestEngine, connectorInstance, connectorInstanceService);
3430
this.assetAgentJobRepository = assetAgentJobRepository;
35-
this.executorService = executorService;
3631
}
3732

3833
@Override
3934
protected void innerStart() throws Exception {
40-
executorService.register(
41-
OPENAEV_EXECUTOR_ID,
42-
OPENAEV_EXECUTOR_TYPE,
43-
OPENAEV_EXECUTOR_NAME,
44-
OPENAEV_EXECUTOR_DOCUMENTATION_LINK,
45-
OPENAEV_EXECUTOR_BACKGROUND_COLOR,
46-
getClass().getResourceAsStream("/img/icon-openaev.png"),
47-
getClass().getResourceAsStream("/img/banner-openaev.png"),
48-
new String[] {
49-
Endpoint.PLATFORM_TYPE.Windows.name(),
50-
Endpoint.PLATFORM_TYPE.Linux.name(),
51-
Endpoint.PLATFORM_TYPE.MacOS.name()
52-
});
5335

5436
this.openAEVExecutorContextService = new OpenAEVExecutorContextService(assetAgentJobRepository);
5537
}

openaev-api/src/main/java/io/openaev/integration/impl/executors/openaev/OpenAEVExecutorIntegrationFactory.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,7 @@ public List<ConnectorInstance> findRelatedInstances() {
6262
@Override
6363
public Integration spawn(ConnectorInstance instance) {
6464
return new OpenAEVExecutorIntegration(
65-
instance,
66-
connectorInstanceService,
67-
executorService,
68-
assetAgentJobRepository,
69-
componentRequestEngine);
65+
instance, connectorInstanceService, assetAgentJobRepository, componentRequestEngine);
7066
}
7167

7268
@Override

openaev-api/src/main/java/io/openaev/integration/impl/injectors/channel/ChannelInjectorIntegration.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import io.openaev.database.model.ConnectorInstance;
44
import io.openaev.database.repository.ArticleRepository;
55
import io.openaev.executors.InjectorContext;
6-
import io.openaev.healthcheck.enums.ExternalServiceDependency;
76
import io.openaev.injectors.channel.ChannelContract;
87
import io.openaev.injectors.channel.ChannelExecutor;
98
import io.openaev.injectors.email.service.EmailService;
@@ -13,7 +12,6 @@
1312
import io.openaev.service.InjectExpectationService;
1413
import io.openaev.service.InjectorService;
1514
import io.openaev.service.connector_instances.ConnectorInstanceService;
16-
import java.util.List;
1715

1816
public class ChannelInjectorIntegration extends IntegrationInMemory {
1917
static final String CHANNEL_INJECTOR_NAME = "Media pressure";
@@ -51,16 +49,6 @@ public ChannelInjectorIntegration(
5149

5250
@Override
5351
protected void innerStart() throws Exception {
54-
injectorService.registerBuiltinInjector(
55-
CHANNEL_INJECTOR_ID,
56-
CHANNEL_INJECTOR_NAME,
57-
this.channelContract,
58-
false,
59-
"media-pressure",
60-
null,
61-
null,
62-
false,
63-
List.of(ExternalServiceDependency.SMTP, ExternalServiceDependency.SMTP));
6452
this.channelExecutor =
6553
new ChannelExecutor(
6654
injectorContext,

openaev-api/src/main/java/io/openaev/integration/impl/injectors/email/EmailInjectorIntegration.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import io.openaev.database.model.ConnectorInstance;
44
import io.openaev.executors.InjectorContext;
5-
import io.openaev.healthcheck.enums.ExternalServiceDependency;
65
import io.openaev.injectors.email.EmailContract;
76
import io.openaev.injectors.email.EmailExecutor;
87
import io.openaev.injectors.email.service.EmailService;
@@ -12,7 +11,6 @@
1211
import io.openaev.service.InjectExpectationService;
1312
import io.openaev.service.InjectorService;
1413
import io.openaev.service.connector_instances.ConnectorInstanceService;
15-
import java.util.List;
1614

1715
public class EmailInjectorIntegration extends IntegrationInMemory {
1816
static final String EMAIL_INJECTOR_NAME = "Email";
@@ -47,16 +45,6 @@ public EmailInjectorIntegration(
4745

4846
@Override
4947
protected void innerStart() throws Exception {
50-
injectorService.registerBuiltinInjector(
51-
EMAIL_INJECTOR_ID,
52-
EMAIL_INJECTOR_NAME,
53-
emailContract,
54-
false,
55-
"communication",
56-
null,
57-
null,
58-
false,
59-
List.of(ExternalServiceDependency.SMTP, ExternalServiceDependency.IMAP));
6048
this.emailExecutor = new EmailExecutor(injectorContext, emailService, injectExpectationService);
6149
}
6250

openaev-api/src/main/java/io/openaev/integration/impl/injectors/manual/ManualInjectorIntegration.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import io.openaev.service.InjectExpectationService;
1111
import io.openaev.service.InjectorService;
1212
import io.openaev.service.connector_instances.ConnectorInstanceService;
13-
import java.util.List;
1413

1514
public class ManualInjectorIntegration extends IntegrationInMemory {
1615
static final String MANUAL_INJECTOR_NAME = "Manual";
@@ -42,16 +41,6 @@ public ManualInjectorIntegration(
4241

4342
@Override
4443
protected void innerStart() throws Exception {
45-
injectorService.registerBuiltinInjector(
46-
MANUAL_INJECTOR_ID,
47-
MANUAL_INJECTOR_NAME,
48-
manualContract,
49-
true,
50-
"generic",
51-
null,
52-
null,
53-
false,
54-
List.of());
5544
this.manualExecutor = new ManualExecutor(injectorContext, injectExpectationService);
5645
}
5746

openaev-api/src/main/java/io/openaev/integration/impl/injectors/openaev/OpenaevInjectorIntegration.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
import io.openaev.service.InjectExpectationService;
1414
import io.openaev.service.InjectorService;
1515
import io.openaev.service.connector_instances.ConnectorInstanceService;
16-
import java.util.List;
17-
import java.util.Map;
1816

1917
public class OpenaevInjectorIntegration extends IntegrationInMemory {
2018
public static final String OPENAEV_INJECTOR_NAME = "OpenAEV Implant";
@@ -54,21 +52,6 @@ public OpenaevInjectorIntegration(
5452

5553
@Override
5654
protected void innerStart() throws Exception {
57-
Map<String, String> executorCommands =
58-
OpenaevImplantCommandBuilder.buildExecutorCommands(openAEVConfig);
59-
Map<String, String> executorClearCommands =
60-
OpenaevImplantCommandBuilder.buildExecutorClearCommands();
61-
62-
injectorService.registerBuiltinInjector(
63-
OPENAEV_INJECTOR_ID,
64-
OPENAEV_INJECTOR_NAME,
65-
openAEVImplantContract,
66-
false,
67-
"simulation-implant",
68-
executorCommands,
69-
executorClearCommands,
70-
true,
71-
List.of());
7255
this.openAEVImplantExecutor =
7356
new OpenAEVImplantExecutor(
7457
injectorContext, assetGroupService, injectExpectationService, injectService);

openaev-api/src/main/java/io/openaev/integration/impl/injectors/opencti/OpenCTIInjectorIntegration.java

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.fasterxml.jackson.core.JsonProcessingException;
44
import io.openaev.database.model.ConnectorInstance;
5-
import io.openaev.database.model.ConnectorType;
65
import io.openaev.executors.InjectorContext;
76
import io.openaev.executors.exception.ExecutorException;
87
import io.openaev.injectors.opencti.OpenCTIContract;
@@ -69,20 +68,6 @@ public OpenCTIInjectorIntegration(
6968

7069
@Override
7170
protected void innerStart() throws Exception {
72-
String injectorId =
73-
connectorInstanceService.getConnectorInstanceConfigurationsByIdAndKey(
74-
connectorInstance.getId(), ConnectorType.INJECTOR.getIdKeyName());
75-
76-
injectorService.registerBuiltinInjector(
77-
injectorId,
78-
OPENCTI_INJECTOR_NAME,
79-
openCTIContract,
80-
true,
81-
"incident-response",
82-
null,
83-
null,
84-
false,
85-
new ArrayList<>());
8671
this.openCTIExecutor =
8772
new OpenCTIExecutor(injectorContext, openCTIService, injectExpectationService);
8873
}

openaev-api/src/main/java/io/openaev/integration/impl/injectors/ovh/OvhInjectorIntegration.java

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.fasterxml.jackson.core.JsonProcessingException;
44
import io.openaev.database.model.ConnectorInstance;
5-
import io.openaev.database.model.ConnectorType;
65
import io.openaev.executors.InjectorContext;
76
import io.openaev.executors.exception.ExecutorException;
87
import io.openaev.injectors.ovh.OvhSmsContract;
@@ -17,7 +16,6 @@
1716
import io.openaev.service.InjectorService;
1817
import io.openaev.service.connector_instances.ConnectorInstanceService;
1918
import java.lang.reflect.InvocationTargetException;
20-
import java.util.List;
2119
import lombok.extern.slf4j.Slf4j;
2220

2321
@Slf4j
@@ -68,20 +66,6 @@ public OvhInjectorIntegration(
6866

6967
@Override
7068
protected void innerStart() throws Exception {
71-
String injectorId =
72-
connectorInstanceService.getConnectorInstanceConfigurationsByIdAndKey(
73-
connectorInstance.getId(), ConnectorType.INJECTOR.getIdKeyName());
74-
75-
injectorService.registerBuiltinInjector(
76-
injectorId,
77-
OVH_SMS_INJECTOR_NAME,
78-
ovhSmsContract,
79-
true,
80-
"communication",
81-
null,
82-
null,
83-
false,
84-
List.of());
8569
OvhSmsService ovhSmsService = new OvhSmsService(this.config);
8670
this.ovhSmsExecutor =
8771
new OvhSmsExecutor(injectorContext, ovhSmsService, injectExpectationService);

0 commit comments

Comments
 (0)