Skip to content

Commit 4bcd433

Browse files
committed
[backend/frontend] Continue
1 parent 5309a08 commit 4bcd433

37 files changed

Lines changed: 412 additions & 376 deletions

openaev-api/src/main/java/io/openaev/api/stix_process/StixApi.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ public class StixApi extends RestBehavior {
6363
public ResponseEntity<?> processBundle(@RequestBody String ctiEvent)
6464
throws ParsingException, ConnectorError, IOException {
6565
String workId = null;
66-
// TODO check the methods to see if the tenant context is used automatically or if we need to
67-
// add it
6866
String tenantId = TenantContext.getCurrentTenant();
6967
try {
7068
JsonNode root = objectMapper.readTree(ctiEvent);
@@ -77,7 +75,7 @@ public ResponseEntity<?> processBundle(@RequestBody String ctiEvent)
7775
// Create scenario from stix bundle
7876
// If no simulation for this scenario is in progress, start an execution right away
7977

80-
Scenario scenario = stixService.processBundle(stixJson);
78+
Scenario scenario = stixService.processBundle(stixJson, tenantId);
8179
openCTIService.acknowledgeProcessedOfCoverage(
8280
workId, "Coverage successfully created or updated", false, tenantId);
8381
// Generate response

openaev-api/src/main/java/io/openaev/config/TenantUriUtils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
public final class TenantUriUtils {
44

55
public static final String TENANT_ID_PATH_VARIABLE = "tenantId";
6-
public static final String TENANT_PREFIX = "/api/tenants/{" + TENANT_ID_PATH_VARIABLE + "}";
6+
public static final String TENANT_BASE_PATH = "/api/tenants/";
7+
public static final String TENANT_PREFIX = TENANT_BASE_PATH + "{" + TENANT_ID_PATH_VARIABLE + "}";
78

89
private TenantUriUtils() {}
910
}
Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,43 @@
11
package io.openaev.opencti.config;
22

3-
import java.util.Map;
3+
import com.fasterxml.jackson.annotation.JsonProperty;
44
import lombok.Data;
5-
import org.springframework.boot.context.properties.ConfigurationProperties;
6-
import org.springframework.stereotype.Component;
7-
8-
/**
9-
* Binds per-tenant OpenCTI configuration from properties of the form:
10-
* openaev.xtm.opencti.<tenantId>.enable / .url / .token / .api-url
11-
*/
12-
@Component
5+
import org.apache.commons.lang3.StringUtils;
6+
137
@Data
14-
@ConfigurationProperties(prefix = "openaev.xtm")
158
public class OpenCTIConfig {
9+
public static final String GRAPHQL_ENDPOINT_URI = "graphql";
10+
11+
@JsonProperty("enable")
12+
private Boolean enable;
13+
14+
@JsonProperty("url")
15+
private String url;
16+
17+
@JsonProperty("api-url")
18+
private String apiUrl;
19+
20+
@JsonProperty("token")
21+
private String token;
22+
23+
public String getApiUrl() {
24+
// Case 1: apiUrl defined
25+
if (apiUrl != null && !apiUrl.isBlank()) {
26+
return apiUrl;
27+
}
28+
// Case 2: fallback to url
29+
if (url == null || url.isBlank()) {
30+
return null;
31+
}
32+
String urlStripped = StringUtils.stripEnd(url, "/");
33+
if (urlStripped.toLowerCase().contains("/graphql")) {
34+
return urlStripped;
35+
}
36+
37+
return String.join("/", urlStripped, GRAPHQL_ENDPOINT_URI);
38+
}
1639

17-
/** Key = tenant ID, value = OpenCTI connection parameters for that tenant. */
18-
private Map<String, OpenCTIParamConfig> opencti;
40+
public String getFormattedUrl() {
41+
return url.endsWith("/") ? url : url + "/";
42+
}
1943
}

openaev-api/src/main/java/io/openaev/opencti/config/OpenCTIParamConfig.java

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.openaev.opencti.config;
2+
3+
import java.util.Map;
4+
import lombok.Data;
5+
import org.springframework.boot.context.properties.ConfigurationProperties;
6+
import org.springframework.stereotype.Component;
7+
8+
@Component
9+
@Data
10+
@ConfigurationProperties(prefix = "openaev.xtm")
11+
public class XtmConfig {
12+
13+
private Map<String, OpenCTIConfig> opencti;
14+
}

openaev-api/src/main/java/io/openaev/opencti/connectors/ConnectorBase.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.openaev.opencti.connectors;
22

33
import com.fasterxml.jackson.annotation.JsonIgnore;
4-
import io.openaev.database.model.Tenant;
54
import java.util.ArrayList;
65
import java.util.List;
76
import lombok.Data;
@@ -15,7 +14,7 @@ public abstract class ConnectorBase {
1514
private boolean playbookCompatible = false;
1615
private String listenCallbackURI;
1716
private volatile String jwks;
18-
private String tenantId = Tenant.DEFAULT_TENANT_UUID;
17+
private String tenantId;
1918

2019
public abstract String getName();
2120

openaev-api/src/main/java/io/openaev/opencti/connectors/Constants.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@
55
import java.util.Set;
66

77
public class Constants {
8+
// -- CAPABILITIES --
9+
public static final Set<Capability> PROCESS_STIX_ROLE_CAPABILITIES =
10+
new HashSet<>(Set.of(Capability.MANAGE_STIX_BUNDLE));
11+
12+
// -- ROLE --
813
public static final String PROCESS_STIX_ROLE_ID = "2c24790b-fa69-4565-8dc8-b00f85ca47d5";
914
public static final String PROCESS_STIX_ROLE_NAME = "STIX bundle processors";
1015
public static final String PROCESS_STIX_ROLE_DESCRIPTION = "Can process STIX bundles via API";
11-
public static final Set<Capability> PROCESS_STIX_ROLE_CAPABILITIES =
12-
new HashSet<>(Set.of(Capability.MANAGE_STIX_BUNDLE));
1316

17+
// -- GROUP --
1418
public static final String PROCESS_STIX_GROUP_ID = "0b4db570-fdf4-44e9-8daa-39130189fec8";
1519
public static final String PROCESS_STIX_GROUP_NAME = "STIX bundle processors";
1620
public static final String PROCESS_STIX_GROUP_DESCRIPTION =
Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,27 @@
11
package io.openaev.opencti.connectors.impl;
22

3-
import static io.openaev.config.TenantUriUtils.TENANT_PREFIX;
3+
import static io.openaev.config.TenantUriUtils.TENANT_BASE_PATH;
44

55
import io.openaev.config.OpenAEVConfig;
6-
import io.openaev.opencti.config.OpenCTIParamConfig;
6+
import io.openaev.opencti.config.OpenCTIConfig;
77
import io.openaev.opencti.connectors.ConnectorBase;
88
import io.openaev.opencti.connectors.ConnectorType;
99
import io.openaev.utils.StringUtils;
1010
import java.util.ArrayList;
1111
import java.util.List;
12+
import java.util.UUID;
1213
import lombok.Getter;
1314
import lombok.Setter;
14-
import org.springframework.beans.factory.annotation.Autowired;
15-
import org.springframework.boot.context.properties.ConfigurationProperties;
16-
import org.springframework.stereotype.Component;
1715

18-
@Component
1916
@Getter
20-
@ConfigurationProperties(prefix = "openaev.xtm.opencti.connector.security-coverage")
2117
public class SecurityCoverageConnector extends ConnectorBase {
22-
// TODO migrate connector and user to match with the new one
23-
private static final String BASE_ID = "68949a7b-c1c2-4649-b3de-7db804ba02bb";
18+
private static final UUID NAMESPACE = UUID.fromString("68949a7b-c1c2-4649-b3de-7db804ba02bb");
19+
private static final String STIX_PROCESS_BUNDLE_PATH = "/stix/process-bundle";
2420

25-
// need to access the base URL for overriding the callback URI
26-
private OpenCTIParamConfig openCTIParamConfig;
27-
private OpenAEVConfig mainConfig;
28-
29-
@Autowired
30-
public void setOpenCTIParamConfig(OpenCTIParamConfig openCTIParamConfig) {
31-
this.openCTIParamConfig = openCTIParamConfig;
32-
}
33-
34-
@Autowired
35-
public void setMainConfig(OpenAEVConfig mainConfig) {
36-
this.mainConfig = mainConfig;
37-
}
21+
@Setter private OpenCTIConfig openCTIConfig;
22+
@Setter private OpenAEVConfig openAEVConfig;
3823

3924
private final ConnectorType type = ConnectorType.INTERNAL_ENRICHMENT;
40-
// TODO update with tenant name at the end
41-
private final String name = "OpenAEV Coverage";
4225
@Setter private volatile String jwks;
4326

4427
public SecurityCoverageConnector() {
@@ -47,38 +30,46 @@ public SecurityCoverageConnector() {
4730
this.setAutoUpdate(true);
4831
}
4932

33+
@Override
34+
public String getName() {
35+
return "OpenAEV Coverage - " + this.getTenantId();
36+
}
37+
5038
@Override
5139
public String getId() {
52-
return BASE_ID + ":" + this.getTenantId();
40+
return UUID.nameUUIDFromBytes((NAMESPACE + ":" + this.getTenantId()).getBytes()).toString();
5341
}
5442

5543
@Override
5644
public String getUrl() {
57-
return openCTIParamConfig.getUrl();
45+
return openCTIConfig.getUrl();
5846
}
5947

6048
@Override
6149
public String getApiUrl() {
62-
return openCTIParamConfig.getApiUrl();
50+
return openCTIConfig.getApiUrl();
6351
}
6452

6553
@Override
6654
public String getToken() {
67-
return openCTIParamConfig.getToken();
55+
return openCTIConfig.getToken();
6856
}
6957

7058
@Override
7159
public boolean shouldRegister() {
72-
return Boolean.TRUE.equals(openCTIParamConfig.getEnable())
73-
&& !StringUtils.isBlank(this.getListenCallbackURI())
74-
&& !StringUtils.isBlank(this.getName())
75-
&& !StringUtils.isBlank(this.getToken())
76-
&& !StringUtils.isBlank(this.getUrl())
77-
&& this.getType() != null;
60+
return openCTIConfig != null
61+
&& Boolean.TRUE.equals(openCTIConfig.getEnable())
62+
&& !StringUtils.isBlank(this.getTenantId())
63+
&& !StringUtils.isBlank(openCTIConfig.getUrl())
64+
&& !StringUtils.isBlank(openCTIConfig.getToken())
65+
&& openAEVConfig != null;
7866
}
7967

8068
@Override
8169
public String getListenCallbackURI() {
82-
return mainConfig.getBaseUrl() + TENANT_PREFIX + this.getTenantId() + "/stix/process-bundle";
70+
return openAEVConfig.getBaseUrl()
71+
+ TENANT_BASE_PATH
72+
+ this.getTenantId()
73+
+ STIX_PROCESS_BUNDLE_PATH;
8374
}
8475
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package io.openaev.opencti.connectors.service;
2+
3+
import io.openaev.database.model.User;
4+
import io.openaev.service.UserService;
5+
import java.util.Optional;
6+
import lombok.RequiredArgsConstructor;
7+
import lombok.extern.slf4j.Slf4j;
8+
import org.springframework.stereotype.Component;
9+
10+
/**
11+
* Handles cleanup of pre-multi-tenant OpenCTI connector service accounts.
12+
*
13+
* <p>Before multi-tenancy, a single connector user was created with email {@code
14+
* connector-<baseId>@openaev.invalid}. After multi-tenancy, each tenant gets its own connector user
15+
* with email {@code connector-opencti-<baseId>:<tenantId>@openaev.invalid}.
16+
*
17+
* <p>This class detects and deletes the legacy user so the new tenant-scoped user can be created
18+
* cleanly.
19+
*
20+
* <p><b>TODO: remove this class once all deployments have been migrated to multi-tenant.</b>
21+
*/
22+
@Component
23+
@RequiredArgsConstructor
24+
@Slf4j
25+
public class LegacyOpenCTIConnectorMigration {
26+
27+
private static final String LEGACY_CONNECTOR_BASE_ID = "68949a7b-c1c2-4649-b3de-7db804ba02bb";
28+
private static final String LEGACY_EMAIL_PATTERN = "connector-%s@openaev.invalid";
29+
30+
private final UserService userService;
31+
32+
/**
33+
* Deletes the legacy (pre-multi-tenant) connector service account if it still exists.
34+
*
35+
* @param newEmail the new tenant-scoped email, used only for logging
36+
*/
37+
public void deleteLegacyConnectorUserIfExists(String newEmail) {
38+
String legacyEmail = LEGACY_EMAIL_PATTERN.formatted(LEGACY_CONNECTOR_BASE_ID);
39+
Optional<User> legacyUser = userService.findByEmailIgnoreCase(legacyEmail);
40+
41+
if (legacyUser.isPresent()) {
42+
log.info(
43+
"Deleting legacy connector service account {} — replaced by tenant-scoped account {}",
44+
legacyEmail,
45+
newEmail);
46+
userService.delete(legacyUser.get().getId());
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)