From 92192934b53928607b311caab295bf15f0491866 Mon Sep 17 00:00:00 2001 From: Damien Goujard Date: Thu, 19 Feb 2026 11:44:54 +0100 Subject: [PATCH 1/5] [backend] Default tenant migration and foreign keys --- .../V4_72__Create_default_tenant.java | 101 ++++++++++++++++++ .../java/io/openaev/database/model/Agent.java | 8 ++ .../java/io/openaev/database/model/Asset.java | 7 ++ .../openaev/database/model/AssetAgentJob.java | 10 ++ .../io/openaev/database/model/AssetGroup.java | 7 ++ .../openaev/database/model/AttackPattern.java | 8 ++ .../io/openaev/database/model/Challenge.java | 7 ++ .../io/openaev/database/model/Channel.java | 7 ++ .../io/openaev/database/model/Collector.java | 7 ++ .../model/ConnectorInstancePersisted.java | 9 ++ .../database/model/CustomDashboard.java | 7 ++ .../io/openaev/database/model/DataPack.java | 10 ++ .../io/openaev/database/model/Document.java | 9 ++ .../io/openaev/database/model/Executor.java | 7 ++ .../io/openaev/database/model/Exercise.java | 7 ++ .../io/openaev/database/model/Finding.java | 7 ++ .../java/io/openaev/database/model/Group.java | 9 ++ .../openaev/database/model/ImportMapper.java | 9 ++ .../io/openaev/database/model/Inject.java | 7 ++ .../io/openaev/database/model/Injector.java | 7 ++ .../database/model/InjectorContract.java | 7 ++ .../database/model/KillChainPhase.java | 7 ++ .../database/model/LessonsTemplate.java | 7 ++ .../openaev/database/model/Organization.java | 7 ++ .../io/openaev/database/model/Payload.java | 7 ++ .../java/io/openaev/database/model/Role.java | 7 ++ .../io/openaev/database/model/Scenario.java | 7 ++ .../java/io/openaev/database/model/Tag.java | 7 ++ .../io/openaev/database/model/TagRule.java | 8 ++ .../java/io/openaev/database/model/Team.java | 7 ++ .../io/openaev/database/model/Tenant.java | 9 ++ .../openaev/database/model/Vulnerability.java | 8 ++ 32 files changed, 338 insertions(+) create mode 100644 openaev-api/src/main/java/io/openaev/migration/V4_72__Create_default_tenant.java diff --git a/openaev-api/src/main/java/io/openaev/migration/V4_72__Create_default_tenant.java b/openaev-api/src/main/java/io/openaev/migration/V4_72__Create_default_tenant.java new file mode 100644 index 00000000000..10d5524e052 --- /dev/null +++ b/openaev-api/src/main/java/io/openaev/migration/V4_72__Create_default_tenant.java @@ -0,0 +1,101 @@ +package io.openaev.migration; + +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; + +import java.sql.Statement; +import java.util.List; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; +import org.springframework.stereotype.Component; + +@Component +public class V4_72__Create_default_tenant extends BaseJavaMigration { + + // Strings to replace in the SQL statement + private static String DEFAULT_TENANT_ID = "[DEFAULT_TENANT_ID]"; + private static String TABLE_FK_TENANT = "[TABLE_FK_TENANT]"; + private static List TABLES = + List.of( + "agents", + "asset_agent_jobs", + "asset_groups", + "assets", + "attack_patterns", + "challenges", + "channels", + "collectors", + "connector_instances", + "custom_dashboards", + "datapacks", + "documents", + "executors", + "exercises", + "findings", + "groups", + "import_mappers", + "injectors", + "injectors_contracts", + "injects", + "kill_chain_phases", + "lessons_templates", + "organizations", + "payloads", + "roles", + "scenarios", + "tag_rules", + "tags", + "teams", + "vulnerabilities"); + + @Override + public void migrate(Context context) throws Exception { + + // SQL statements to execute with Strings to replace + String addDefaultTenant = + """ + DO $$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM tenants) THEN + INSERT INTO tenants(tenant_id, tenant_name, tenant_description) VALUES ('[DEFAULT_TENANT_ID]', 'First default tenant auto created to rename', 'First default tenant auto created to rename'); + END IF; + END $$; + """; + String addForeignKeyTenant = + """ + ALTER TABLE [TABLE_FK_TENANT] + ADD COLUMN tenant_id VARCHAR(255) NOT NULL DEFAULT '[DEFAULT_TENANT_ID]', + ADD CONSTRAINT fk_tenant_id FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id) ON DELETE CASCADE; + """; + String addIndexTenant = + "CREATE INDEX IF NOT EXISTS idx_tenant_id ON [TABLE_FK_TENANT](tenant_id);"; + + try (Statement statement = context.getConnection().createStatement()) { + // Add default tenant + statement.execute(addDefaultTenant.replace(DEFAULT_TENANT_ID, DEFAULT_TENANT_UUID)); + // Add deleted_at in tenants for soft delete + statement.execute("ALTER TABLE tenants ADD tenant_deleted_at TIMESTAMP WITH TIME ZONE;"); + // Add foreign keys with index, auto set default tenant id with default value + for (String table : TABLES) { + statement.addBatch( + addForeignKeyTenant + .replace(DEFAULT_TENANT_ID, DEFAULT_TENANT_UUID) + .replace(TABLE_FK_TENANT, table)); + statement.addBatch(addIndexTenant.replace(TABLE_FK_TENANT, table)); + } + statement.executeBatch(); + // Add linked table for users and tenants + statement.execute( + """ + CREATE TABLE IF NOT EXISTS users_tenants ( + user_id VARCHAR(255) NOT NULL, + tenant_id VARCHAR(255) NOT NULL, + PRIMARY KEY (user_id, tenant_id), + CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE, + CONSTRAINT fk_tenant_id FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id) ON DELETE CASCADE + ); + """); + statement.execute("CREATE INDEX IF NOT EXISTS idx_user_id ON users_tenants(user_id);"); + statement.execute("CREATE INDEX IF NOT EXISTS idx_tenant_id ON users_tenants(tenant_id);"); + } + } +} diff --git a/openaev-model/src/main/java/io/openaev/database/model/Agent.java b/openaev-model/src/main/java/io/openaev/database/model/Agent.java index 33a933f162e..37b3271c29f 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Agent.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Agent.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -144,6 +145,13 @@ public boolean isActive() { @Transient private final ResourceType resourceType = ResourceType.AGENT; + // Ignore json and not null + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Override public int hashCode() { return Objects.hash(id); diff --git a/openaev-model/src/main/java/io/openaev/database/model/Asset.java b/openaev-model/src/main/java/io/openaev/database/model/Asset.java index ea7b33c839a..5ff0ebcb521 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Asset.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Asset.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static jakarta.persistence.DiscriminatorType.STRING; import static java.time.Instant.now; import static lombok.AccessLevel.NONE; @@ -63,6 +64,12 @@ public class Asset implements Base { @JsonProperty("asset_external_reference") private String externalReference; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + // -- TAG -- @Schema(implementation = String[].class) diff --git a/openaev-model/src/main/java/io/openaev/database/model/AssetAgentJob.java b/openaev-model/src/main/java/io/openaev/database/model/AssetAgentJob.java index c3dd007301a..efe11e8708b 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/AssetAgentJob.java +++ b/openaev-model/src/main/java/io/openaev/database/model/AssetAgentJob.java @@ -1,5 +1,8 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; + +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.openaev.database.audit.ModelBaseListener; @@ -7,6 +10,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import java.util.Objects; import lombok.Data; import lombok.Getter; @@ -48,6 +52,12 @@ public class AssetAgentJob implements Base { @NotBlank private String command; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Override public String toString() { return id; diff --git a/openaev-model/src/main/java/io/openaev/database/model/AssetGroup.java b/openaev-model/src/main/java/io/openaev/database/model/AssetGroup.java index fea2dd0a36e..de86b67bd1f 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/AssetGroup.java +++ b/openaev-model/src/main/java/io/openaev/database/model/AssetGroup.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import static lombok.AccessLevel.NONE; @@ -57,6 +58,12 @@ public class AssetGroup implements Base { @JsonProperty("asset_group_external_reference") private String externalReference; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + // -- ASSET -- @Type(JsonType.class) diff --git a/openaev-model/src/main/java/io/openaev/database/model/AttackPattern.java b/openaev-model/src/main/java/io/openaev/database/model/AttackPattern.java index 22e29f91557..9ab74d4e507 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/AttackPattern.java +++ b/openaev-model/src/main/java/io/openaev/database/model/AttackPattern.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -14,6 +15,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -101,6 +103,12 @@ public class AttackPattern implements Base { @JsonProperty("attack_pattern_kill_chain_phases") private List killChainPhases = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.ATTACK_PATTERN; diff --git a/openaev-model/src/main/java/io/openaev/database/model/Challenge.java b/openaev-model/src/main/java/io/openaev/database/model/Challenge.java index fe2c66d1fa5..ad21e27a0fa 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Challenge.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Challenge.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -97,6 +98,12 @@ public class Challenge implements Base { @JsonProperty("challenge_documents") private List documents = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Transient private List exerciseIds = new ArrayList<>(); @Transient private List scenarioIds = new ArrayList<>(); diff --git a/openaev-model/src/main/java/io/openaev/database/model/Channel.java b/openaev-model/src/main/java/io/openaev/database/model/Channel.java index df2dcf2454b..a32ea429565 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Channel.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Channel.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -90,6 +91,12 @@ public class Channel implements Base { @Schema(implementation = String.class) private Document logoLight; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.CHANNEL; diff --git a/openaev-model/src/main/java/io/openaev/database/model/Collector.java b/openaev-model/src/main/java/io/openaev/database/model/Collector.java index 89a9ba64539..c8d89933d91 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Collector.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Collector.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -71,6 +72,12 @@ public class Collector extends BaseConnectorEntity { @Type(JsonType.class) private ObjectNode state; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.COLLECTOR; diff --git a/openaev-model/src/main/java/io/openaev/database/model/ConnectorInstancePersisted.java b/openaev-model/src/main/java/io/openaev/database/model/ConnectorInstancePersisted.java index 08e82763a33..6d4750529b8 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/ConnectorInstancePersisted.java +++ b/openaev-model/src/main/java/io/openaev/database/model/ConnectorInstancePersisted.java @@ -1,6 +1,9 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; + import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.openaev.database.audit.ModelBaseListener; import jakarta.persistence.*; @@ -90,6 +93,12 @@ public class ConnectorInstancePersisted extends ConnectorInstance implements Bas @NotNull private Set configurations = new HashSet<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Override public String getClassName() { if (this.getCatalogConnector() != null) { diff --git a/openaev-model/src/main/java/io/openaev/database/model/CustomDashboard.java b/openaev-model/src/main/java/io/openaev/database/model/CustomDashboard.java index 3bbd0b7981d..29e411b331e 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/CustomDashboard.java +++ b/openaev-model/src/main/java/io/openaev/database/model/CustomDashboard.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static jakarta.persistence.FetchType.LAZY; import static java.time.Instant.now; import static java.util.function.Function.identity; @@ -68,6 +69,12 @@ public class CustomDashboard implements Base { @OrderBy("id ASC") private List parameters = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + public CustomDashboard addParameter( @NotBlank final String name, @NotBlank final CustomDashboardParameterType type) { if (this.getParameters().stream().anyMatch(p -> p.getType().equals(type) && type.uniq)) { diff --git a/openaev-model/src/main/java/io/openaev/database/model/DataPack.java b/openaev-model/src/main/java/io/openaev/database/model/DataPack.java index 1089426247a..0a6b248bc87 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/DataPack.java +++ b/openaev-model/src/main/java/io/openaev/database/model/DataPack.java @@ -1,8 +1,12 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; + +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Getter; import lombok.Setter; @@ -16,4 +20,10 @@ public class DataPack { @JsonProperty("datapack_id") @NotBlank private String id; + + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); } diff --git a/openaev-model/src/main/java/io/openaev/database/model/Document.java b/openaev-model/src/main/java/io/openaev/database/model/Document.java index e8ffc002890..21fedd08b3a 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Document.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Document.java @@ -1,5 +1,7 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; + import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -9,6 +11,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -108,6 +111,12 @@ public class Document implements Base { @JsonIgnore private Set challenges = new HashSet<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @OneToMany(mappedBy = "document", fetch = FetchType.LAZY) @JsonIgnore private Set injectDocuments = new HashSet<>(); diff --git a/openaev-model/src/main/java/io/openaev/database/model/Executor.java b/openaev-model/src/main/java/io/openaev/database/model/Executor.java index 44ed4f9e4f6..b299f46ccf4 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Executor.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Executor.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -51,6 +52,12 @@ public class Executor extends BaseConnectorEntity { @JsonProperty("executor_background_color") private String backgroundColor; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Column(name = "executor_created_at") @JsonProperty("executor_created_at") @NotNull diff --git a/openaev-model/src/main/java/io/openaev/database/model/Exercise.java b/openaev-model/src/main/java/io/openaev/database/model/Exercise.java index ec90cb59541..9416fee6c60 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Exercise.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Exercise.java @@ -2,6 +2,7 @@ import static io.openaev.database.model.Grant.GRANT_TYPE.OBSERVER; import static io.openaev.database.model.Grant.GRANT_TYPE.PLANNER; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static io.openaev.helper.UserHelper.getUsersByType; import static java.time.Instant.now; import static java.util.Optional.ofNullable; @@ -158,6 +159,12 @@ public class Exercise implements GrantableBase { @JsonProperty("exercise_lessons_anonymized") private boolean lessonsAnonymized = false; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + // -- SCENARIO -- @Getter diff --git a/openaev-model/src/main/java/io/openaev/database/model/Finding.java b/openaev-model/src/main/java/io/openaev/database/model/Finding.java index efa91885936..3816657d488 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Finding.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Finding.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -80,6 +81,12 @@ public class Finding implements Base { @Queryable(filterable = true, dynamicValues = true, path = "tags.id") private Set tags = new HashSet<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + // -- RELATION -- @ManyToOne(fetch = FetchType.LAZY) diff --git a/openaev-model/src/main/java/io/openaev/database/model/Group.java b/openaev-model/src/main/java/io/openaev/database/model/Group.java index a855dc61155..80828d1f4a8 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Group.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Group.java @@ -1,5 +1,7 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; + import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -11,6 +13,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import java.util.*; import lombok.Getter; import lombok.Setter; @@ -77,6 +80,12 @@ public class Group implements Base { @JsonProperty("group_roles") private List roles = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.USER_GROUP; diff --git a/openaev-model/src/main/java/io/openaev/database/model/ImportMapper.java b/openaev-model/src/main/java/io/openaev/database/model/ImportMapper.java index 7657e767f5d..f05863d3f89 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/ImportMapper.java +++ b/openaev-model/src/main/java/io/openaev/database/model/ImportMapper.java @@ -1,5 +1,8 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; + +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.openaev.annotation.Queryable; import io.openaev.database.audit.ModelBaseListener; @@ -46,6 +49,12 @@ public class ImportMapper implements Base { @JsonProperty("import_mapper_inject_importers") private List injectImporters = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @CreationTimestamp @Column(name = "mapper_created_at") @JsonProperty("import_mapper_created_at") diff --git a/openaev-model/src/main/java/io/openaev/database/model/Inject.java b/openaev-model/src/main/java/io/openaev/database/model/Inject.java index 9361c1b5000..7e25275ec34 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Inject.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Inject.java @@ -1,6 +1,7 @@ package io.openaev.database.model; import static io.openaev.database.model.CollectExecutionStatus.COLLECTING; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static io.openaev.database.specification.InjectSpecification.VALID_TESTABLE_TYPES; import static java.time.Instant.now; import static java.util.Optional.ofNullable; @@ -307,6 +308,12 @@ public void setAssetGroups(List assetGroups) { @OneToMany(mappedBy = "inject", cascade = CascadeType.ALL, orphanRemoval = true) private List findings = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter @Setter @Transient private boolean isListened = true; @Getter(onMethod_ = @JsonIgnore) diff --git a/openaev-model/src/main/java/io/openaev/database/model/Injector.java b/openaev-model/src/main/java/io/openaev/database/model/Injector.java index ce2bb4baf60..2ab245fb756 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Injector.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Injector.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -87,6 +88,12 @@ public class Injector extends BaseConnectorEntity { @JsonIgnore private List contracts = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.INJECTOR; diff --git a/openaev-model/src/main/java/io/openaev/database/model/InjectorContract.java b/openaev-model/src/main/java/io/openaev/database/model/InjectorContract.java index 433140db04e..76e8b258ee4 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/InjectorContract.java +++ b/openaev-model/src/main/java/io/openaev/database/model/InjectorContract.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import static java.util.Optional.ofNullable; import static lombok.AccessLevel.NONE; @@ -184,6 +185,12 @@ public void setVulnerabilities(Set vulnerabilities) { @Queryable(filterable = true) private boolean isImportAvailable; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.INJECTOR_CONTRACT; diff --git a/openaev-model/src/main/java/io/openaev/database/model/KillChainPhase.java b/openaev-model/src/main/java/io/openaev/database/model/KillChainPhase.java index 27a080f1ceb..8e9472e6898 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/KillChainPhase.java +++ b/openaev-model/src/main/java/io/openaev/database/model/KillChainPhase.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -78,6 +79,12 @@ public class KillChainPhase implements Base { @NotNull private Instant updatedAt = now(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.KILL_CHAIN_PHASE; diff --git a/openaev-model/src/main/java/io/openaev/database/model/LessonsTemplate.java b/openaev-model/src/main/java/io/openaev/database/model/LessonsTemplate.java index da70eb8c783..269e61357d3 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/LessonsTemplate.java +++ b/openaev-model/src/main/java/io/openaev/database/model/LessonsTemplate.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -55,6 +56,12 @@ public class LessonsTemplate implements Base { @JsonIgnore private List categories = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Override public boolean isUserHasAccess(User user) { return user.isAdmin(); diff --git a/openaev-model/src/main/java/io/openaev/database/model/Organization.java b/openaev-model/src/main/java/io/openaev/database/model/Organization.java index 646dc524777..132ac8124a7 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Organization.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Organization.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import static java.util.stream.StreamSupport.stream; @@ -75,6 +76,12 @@ public class Organization implements Base { @JsonProperty("organization_tags") private Set tags = new HashSet<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + // region transient private transient List injects = new ArrayList<>(); diff --git a/openaev-model/src/main/java/io/openaev/database/model/Payload.java b/openaev-model/src/main/java/io/openaev/database/model/Payload.java index 3aa474325d5..a0cb8a5bc97 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Payload.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Payload.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static jakarta.persistence.DiscriminatorType.STRING; import static java.time.Instant.now; import static lombok.AccessLevel.NONE; @@ -191,6 +192,12 @@ public enum PAYLOAD_EXECUTION_ARCH { @NotNull private PAYLOAD_EXECUTION_ARCH executionArch = Payload.PAYLOAD_EXECUTION_ARCH.ALL_ARCHITECTURES; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + // -- COLLECTOR -- @ManyToOne(fetch = FetchType.LAZY) diff --git a/openaev-model/src/main/java/io/openaev/database/model/Role.java b/openaev-model/src/main/java/io/openaev/database/model/Role.java index 07d1f908c26..b720db44584 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Role.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Role.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -66,4 +67,10 @@ public class Role implements Base { @NotNull @Schema(description = "Update date of the role", accessMode = Schema.AccessMode.READ_ONLY) private Instant updatedAt = now(); + + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); } diff --git a/openaev-model/src/main/java/io/openaev/database/model/Scenario.java b/openaev-model/src/main/java/io/openaev/database/model/Scenario.java index eff4f18b0ab..f727c0305fd 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Scenario.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Scenario.java @@ -2,6 +2,7 @@ import static io.openaev.database.model.Grant.GRANT_TYPE.OBSERVER; import static io.openaev.database.model.Grant.GRANT_TYPE.PLANNER; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static io.openaev.helper.UserHelper.getUsersByType; import static java.time.Instant.now; import static lombok.AccessLevel.NONE; @@ -368,6 +369,12 @@ public void setExercises(List exercises) { @JsonProperty("scenario_lessons_anonymized") private boolean lessonsAnonymized = false; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.SCENARIO; diff --git a/openaev-model/src/main/java/io/openaev/database/model/Tag.java b/openaev-model/src/main/java/io/openaev/database/model/Tag.java index 9d792f01789..a4827dba2bb 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Tag.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Tag.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -100,6 +101,12 @@ public class Tag implements Base { @UpdateTimestamp private Instant updatedAt = now(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.TAG; diff --git a/openaev-model/src/main/java/io/openaev/database/model/TagRule.java b/openaev-model/src/main/java/io/openaev/database/model/TagRule.java index 5ab37d26834..3c08f370327 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/TagRule.java +++ b/openaev-model/src/main/java/io/openaev/database/model/TagRule.java @@ -1,6 +1,7 @@ package io.openaev.database.model; import static io.openaev.database.model.Tag.*; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; @@ -8,6 +9,7 @@ import io.openaev.database.audit.ModelBaseListener; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -57,6 +59,12 @@ public class TagRule implements Base { @JsonProperty("tag_rule_asset_groups") private List assetGroups = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.TAG_RULE; diff --git a/openaev-model/src/main/java/io/openaev/database/model/Team.java b/openaev-model/src/main/java/io/openaev/database/model/Team.java index a3b16683bce..bf7159ea8a7 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Team.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Team.java @@ -1,5 +1,6 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -73,6 +74,12 @@ public class Team implements Base { @UpdateTimestamp private Instant updatedAt = now(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @ArraySchema( schema = @Schema(description = "IDs of the tags of the team", implementation = String.class)) @Queryable(filterable = true, dynamicValues = true, path = "tags.id") diff --git a/openaev-model/src/main/java/io/openaev/database/model/Tenant.java b/openaev-model/src/main/java/io/openaev/database/model/Tenant.java index e87c67e17fc..c737e362479 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Tenant.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Tenant.java @@ -21,6 +21,9 @@ @EntityListeners(ModelBaseListener.class) public class Tenant implements Base { + // Same default ID for XTM HUB and OpenAEV instances + public static String DEFAULT_TENANT_UUID = "2cffad3a-0001-4078-b0e2-ef74274022c3"; + @Id @GeneratedValue(generator = "UUID") @UuidGenerator @@ -55,4 +58,10 @@ public class Tenant implements Base { @NotNull @JsonProperty("tenant_updated_at") private Instant updatedAt = now(); + + public Tenant() {} + + public Tenant(String id) { + this.id = id; + } } diff --git a/openaev-model/src/main/java/io/openaev/database/model/Vulnerability.java b/openaev-model/src/main/java/io/openaev/database/model/Vulnerability.java index 6f4a56374b2..36963721f2c 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Vulnerability.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Vulnerability.java @@ -1,5 +1,7 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; + import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -112,6 +114,12 @@ public enum VulnerabilityStatus { @JsonProperty("vulnerabilities_cwes") private List cwes = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + // -- AUDIT -- @CreationTimestamp From f3ed891929169981f2a8e5600b36e2b68ff35b67 Mon Sep 17 00:00:00 2001 From: Damien Goujard Date: Thu, 19 Feb 2026 14:21:40 +0100 Subject: [PATCH 2/5] [backend] Fix rebase --- ...te_default_tenant.java => V4_73__Create_default_tenant.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename openaev-api/src/main/java/io/openaev/migration/{V4_72__Create_default_tenant.java => V4_73__Create_default_tenant.java} (98%) diff --git a/openaev-api/src/main/java/io/openaev/migration/V4_72__Create_default_tenant.java b/openaev-api/src/main/java/io/openaev/migration/V4_73__Create_default_tenant.java similarity index 98% rename from openaev-api/src/main/java/io/openaev/migration/V4_72__Create_default_tenant.java rename to openaev-api/src/main/java/io/openaev/migration/V4_73__Create_default_tenant.java index 10d5524e052..02f81d9d71a 100644 --- a/openaev-api/src/main/java/io/openaev/migration/V4_72__Create_default_tenant.java +++ b/openaev-api/src/main/java/io/openaev/migration/V4_73__Create_default_tenant.java @@ -9,7 +9,7 @@ import org.springframework.stereotype.Component; @Component -public class V4_72__Create_default_tenant extends BaseJavaMigration { +public class V4_73__Create_default_tenant extends BaseJavaMigration { // Strings to replace in the SQL statement private static String DEFAULT_TENANT_ID = "[DEFAULT_TENANT_ID]"; From 7312c6c915d3c906293986fdc5581ee0ea2ba92a Mon Sep 17 00:00:00 2001 From: Damien Goujard Date: Mon, 23 Feb 2026 11:16:43 +0100 Subject: [PATCH 3/5] [backend] Comments --- ..._tenant.java => V4_74__Create_default_tenant.java} | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) rename openaev-api/src/main/java/io/openaev/migration/{V4_73__Create_default_tenant.java => V4_74__Create_default_tenant.java} (89%) diff --git a/openaev-api/src/main/java/io/openaev/migration/V4_73__Create_default_tenant.java b/openaev-api/src/main/java/io/openaev/migration/V4_74__Create_default_tenant.java similarity index 89% rename from openaev-api/src/main/java/io/openaev/migration/V4_73__Create_default_tenant.java rename to openaev-api/src/main/java/io/openaev/migration/V4_74__Create_default_tenant.java index 02f81d9d71a..0d3891731f5 100644 --- a/openaev-api/src/main/java/io/openaev/migration/V4_73__Create_default_tenant.java +++ b/openaev-api/src/main/java/io/openaev/migration/V4_74__Create_default_tenant.java @@ -9,7 +9,7 @@ import org.springframework.stereotype.Component; @Component -public class V4_73__Create_default_tenant extends BaseJavaMigration { +public class V4_74__Create_default_tenant extends BaseJavaMigration { // Strings to replace in the SQL statement private static String DEFAULT_TENANT_ID = "[DEFAULT_TENANT_ID]"; @@ -52,14 +52,7 @@ public void migrate(Context context) throws Exception { // SQL statements to execute with Strings to replace String addDefaultTenant = - """ - DO $$ - BEGIN - IF NOT EXISTS (SELECT 1 FROM tenants) THEN - INSERT INTO tenants(tenant_id, tenant_name, tenant_description) VALUES ('[DEFAULT_TENANT_ID]', 'First default tenant auto created to rename', 'First default tenant auto created to rename'); - END IF; - END $$; - """; + "INSERT INTO tenants(tenant_id, tenant_name, tenant_description) VALUES ('[DEFAULT_TENANT_ID]', 'First default tenant auto created to rename', 'First default tenant auto created to rename');"; String addForeignKeyTenant = """ ALTER TABLE [TABLE_FK_TENANT] From 4e0afc9a35c3ba69d98db60f4faac943f08d57da Mon Sep 17 00:00:00 2001 From: Damien Goujard Date: Mon, 23 Feb 2026 17:43:41 +0100 Subject: [PATCH 4/5] [backend] Add context for future --- .../java/io/openaev/context/TenantContext.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 openaev-model/src/main/java/io/openaev/context/TenantContext.java diff --git a/openaev-model/src/main/java/io/openaev/context/TenantContext.java b/openaev-model/src/main/java/io/openaev/context/TenantContext.java new file mode 100644 index 00000000000..3d7acd5eb76 --- /dev/null +++ b/openaev-model/src/main/java/io/openaev/context/TenantContext.java @@ -0,0 +1,18 @@ +package io.openaev.context; + +import io.openaev.database.model.Tenant; + +public class TenantContext { + + private static final ThreadLocal CURRENT_TENANT = new ThreadLocal<>(); + + public static String getCurrentTenant() { + return Tenant.DEFAULT_TENANT_UUID; + // return CURRENT_TENANT.get(); + } + + // TODO multi-tenancy: set with Front URL instead of default UUID and update the return above + public static void setCurrentTenant(String tenant) { + CURRENT_TENANT.set(tenant); + } +} From ba174ba5fb14006ba23b3a04c160727c90dbf76a Mon Sep 17 00:00:00 2001 From: Damien Goujard Date: Tue, 24 Feb 2026 15:39:04 +0100 Subject: [PATCH 5/5] [backend] Romuald's comments --- .../V4_74__Create_default_tenant.java | 43 ++++++++++--------- .../database/model/InjectorContract.java | 7 --- .../io/openaev/database/model/Mitigation.java | 8 ++++ .../database/model/NotificationRule.java | 8 ++++ .../io/openaev/database/model/Tenant.java | 4 ++ 5 files changed, 42 insertions(+), 28 deletions(-) diff --git a/openaev-api/src/main/java/io/openaev/migration/V4_74__Create_default_tenant.java b/openaev-api/src/main/java/io/openaev/migration/V4_74__Create_default_tenant.java index 0d3891731f5..cf5156d977d 100644 --- a/openaev-api/src/main/java/io/openaev/migration/V4_74__Create_default_tenant.java +++ b/openaev-api/src/main/java/io/openaev/migration/V4_74__Create_default_tenant.java @@ -12,8 +12,6 @@ public class V4_74__Create_default_tenant extends BaseJavaMigration { // Strings to replace in the SQL statement - private static String DEFAULT_TENANT_ID = "[DEFAULT_TENANT_ID]"; - private static String TABLE_FK_TENANT = "[TABLE_FK_TENANT]"; private static List TABLES = List.of( "agents", @@ -34,11 +32,12 @@ public class V4_74__Create_default_tenant extends BaseJavaMigration { "groups", "import_mappers", "injectors", - "injectors_contracts", "injects", "kill_chain_phases", "lessons_templates", + "mitigations", "organizations", + "notification_rules", "payloads", "roles", "scenarios", @@ -49,31 +48,33 @@ public class V4_74__Create_default_tenant extends BaseJavaMigration { @Override public void migrate(Context context) throws Exception { - - // SQL statements to execute with Strings to replace - String addDefaultTenant = - "INSERT INTO tenants(tenant_id, tenant_name, tenant_description) VALUES ('[DEFAULT_TENANT_ID]', 'First default tenant auto created to rename', 'First default tenant auto created to rename');"; - String addForeignKeyTenant = - """ - ALTER TABLE [TABLE_FK_TENANT] - ADD COLUMN tenant_id VARCHAR(255) NOT NULL DEFAULT '[DEFAULT_TENANT_ID]', - ADD CONSTRAINT fk_tenant_id FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id) ON DELETE CASCADE; - """; - String addIndexTenant = - "CREATE INDEX IF NOT EXISTS idx_tenant_id ON [TABLE_FK_TENANT](tenant_id);"; - try (Statement statement = context.getConnection().createStatement()) { // Add default tenant - statement.execute(addDefaultTenant.replace(DEFAULT_TENANT_ID, DEFAULT_TENANT_UUID)); + statement.execute( + String.format( + """ + INSERT INTO tenants(tenant_id, tenant_name, tenant_description) + VALUES ('%s', 'First default tenant auto created to rename', 'First default tenant auto created to rename'); + """, + DEFAULT_TENANT_UUID)); // Add deleted_at in tenants for soft delete statement.execute("ALTER TABLE tenants ADD tenant_deleted_at TIMESTAMP WITH TIME ZONE;"); // Add foreign keys with index, auto set default tenant id with default value for (String table : TABLES) { statement.addBatch( - addForeignKeyTenant - .replace(DEFAULT_TENANT_ID, DEFAULT_TENANT_UUID) - .replace(TABLE_FK_TENANT, table)); - statement.addBatch(addIndexTenant.replace(TABLE_FK_TENANT, table)); + String.format( + """ + ALTER TABLE %s + ADD COLUMN tenant_id VARCHAR(255) NOT NULL DEFAULT '%s', + ADD CONSTRAINT fk_tenant_id FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id) ON DELETE CASCADE; + """, + table, DEFAULT_TENANT_UUID)); + statement.addBatch( + String.format( + """ + CREATE INDEX IF NOT EXISTS idx_tenant_id ON %s(tenant_id); + """, + table)); } statement.executeBatch(); // Add linked table for users and tenants diff --git a/openaev-model/src/main/java/io/openaev/database/model/InjectorContract.java b/openaev-model/src/main/java/io/openaev/database/model/InjectorContract.java index 76e8b258ee4..433140db04e 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/InjectorContract.java +++ b/openaev-model/src/main/java/io/openaev/database/model/InjectorContract.java @@ -1,6 +1,5 @@ package io.openaev.database.model; -import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; import static java.util.Optional.ofNullable; import static lombok.AccessLevel.NONE; @@ -185,12 +184,6 @@ public void setVulnerabilities(Set vulnerabilities) { @Queryable(filterable = true) private boolean isImportAvailable; - @ManyToOne - @JoinColumn(name = "tenant_id") - @JsonIgnore - @NotNull - private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); - @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.INJECTOR_CONTRACT; diff --git a/openaev-model/src/main/java/io/openaev/database/model/Mitigation.java b/openaev-model/src/main/java/io/openaev/database/model/Mitigation.java index 214a2133b5b..72e592b5870 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Mitigation.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Mitigation.java @@ -1,7 +1,9 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; import static java.time.Instant.now; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.hypersistence.utils.hibernate.type.array.StringArrayType; @@ -85,4 +87,10 @@ public class Mitigation implements Base { @JsonSerialize(using = MultiIdListSerializer.class) @JsonProperty("mitigation_attack_patterns") private List attackPatterns = new ArrayList<>(); + + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); } diff --git a/openaev-model/src/main/java/io/openaev/database/model/NotificationRule.java b/openaev-model/src/main/java/io/openaev/database/model/NotificationRule.java index 222fec9b1e6..88eb9f3c8f5 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/NotificationRule.java +++ b/openaev-model/src/main/java/io/openaev/database/model/NotificationRule.java @@ -1,5 +1,7 @@ package io.openaev.database.model; +import static io.openaev.database.model.Tenant.DEFAULT_TENANT_UUID; + import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.openaev.annotation.Queryable; @@ -62,6 +64,12 @@ public class NotificationRule implements Base { @Queryable(searchable = true, filterable = true, path = "owner.id") private User owner; + @ManyToOne + @JoinColumn(name = "tenant_id") + @JsonIgnore + @NotNull + private Tenant tenant = new Tenant(DEFAULT_TENANT_UUID); + @Getter(onMethod_ = @JsonIgnore) @Transient private final ResourceType resourceType = ResourceType.NOTIFICATION_RULE; diff --git a/openaev-model/src/main/java/io/openaev/database/model/Tenant.java b/openaev-model/src/main/java/io/openaev/database/model/Tenant.java index c737e362479..b92ddbb0982 100644 --- a/openaev-model/src/main/java/io/openaev/database/model/Tenant.java +++ b/openaev-model/src/main/java/io/openaev/database/model/Tenant.java @@ -59,6 +59,10 @@ public class Tenant implements Base { @JsonProperty("tenant_updated_at") private Instant updatedAt = now(); + @Column(name = "tenant_deleted_at") + @JsonProperty("tenant_deleted_at") + private Instant deletedAt; + public Tenant() {} public Tenant(String id) {