From ec7ad9a0e86a6a0616acc6cc670e3d4e4ee90067 Mon Sep 17 00:00:00 2001 From: deiflaender Date: Tue, 7 Mar 2023 16:51:00 +0100 Subject: [PATCH] RED-6254: Store all connection information in master database --- .../multitenancy/AzureStorageConnection.java | 17 +++ .../multitenancy/DatabaseConnection.java | 28 +++++ .../multitenancy/S3StorageConnection.java | 21 ++++ .../model/multitenancy/SearchConnection.java | 24 ++++ .../api/model/multitenancy/TenantRequest.java | 11 +- .../model/multitenancy/TenantResponse.java | 7 +- .../v1/api/resources/TenantsResource.java | 4 + .../entity/AzureStorageConnectionEntity.java | 23 ++++ .../entity/DatabaseConnectionEntity.java | 38 +++++-- .../entity/S3StorageConnectionEntity.java | 31 ++++++ .../entity/SearchConnectionEntity.java | 40 +++++++ .../multitenancy/entity/TenantEntity.java | 13 ++- ...rceBasedMultiTenantConnectionProvider.java | 12 +- .../v1/processor/utils/jdbc/JDBCUtils.java | 48 ++++++++ .../server/controller/TenantsController.java | 6 + .../TenantSpringLiquibaseExecutor.java | 5 +- .../service/TenantManagementService.java | 104 ++++++++++++++++-- .../3-detailed-db-connection.changelog.yaml | 81 ++++++++++---- .../AbstractPersistenceServerServiceTest.java | 40 ++++++- 19 files changed, 493 insertions(+), 60 deletions(-) create mode 100644 persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/AzureStorageConnection.java create mode 100644 persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/DatabaseConnection.java create mode 100644 persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/S3StorageConnection.java create mode 100644 persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/SearchConnection.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/AzureStorageConnectionEntity.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/S3StorageConnectionEntity.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/SearchConnectionEntity.java create mode 100644 persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/jdbc/JDBCUtils.java diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/AzureStorageConnection.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/AzureStorageConnection.java new file mode 100644 index 000000000..a2276924e --- /dev/null +++ b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/AzureStorageConnection.java @@ -0,0 +1,17 @@ +package com.iqser.red.service.persistence.service.v1.api.model.multitenancy; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AzureStorageConnection { + + private String connectionString; + private String containerName; + +} diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/DatabaseConnection.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/DatabaseConnection.java new file mode 100644 index 000000000..feff472ac --- /dev/null +++ b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/DatabaseConnection.java @@ -0,0 +1,28 @@ +package com.iqser.red.service.persistence.service.v1.api.model.multitenancy; + +import java.util.HashMap; +import java.util.Map; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabaseConnection { + + private String driver; + private String host; + private String port; + private String database; + private String schema; + private String username; + private String password; + + @Builder.Default + private Map params = new HashMap<>(); + +} diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/S3StorageConnection.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/S3StorageConnection.java new file mode 100644 index 000000000..a80a84d26 --- /dev/null +++ b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/S3StorageConnection.java @@ -0,0 +1,21 @@ +package com.iqser.red.service.persistence.service.v1.api.model.multitenancy; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class S3StorageConnection { + + private String key; + private String secret; + private String signerType; + private String bucketName; + private String region; + private String endpoint; + +} diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/SearchConnection.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/SearchConnection.java new file mode 100644 index 000000000..4295e1efb --- /dev/null +++ b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/SearchConnection.java @@ -0,0 +1,24 @@ +package com.iqser.red.service.persistence.service.v1.api.model.multitenancy; + +import java.util.Set; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SearchConnection { + + private Set hosts; + private int port = 9300; + private String scheme; + private String username; + private String password; + private String numberOfShards; + private String numberOfReplicas; + +} diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/TenantRequest.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/TenantRequest.java index f16b42430..fbb203c9e 100644 --- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/TenantRequest.java +++ b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/TenantRequest.java @@ -18,11 +18,10 @@ public class TenantRequest { @NotBlank private String displayName; private String guid; - @NotBlank - private String jdbcUrl; - @NotBlank - private String user; - @NotBlank - private String password; + + private DatabaseConnection databaseConnection; + private SearchConnection searchConnection; + private AzureStorageConnection azureStorageConnection; + private S3StorageConnection s3StorageConnection; } diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/TenantResponse.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/TenantResponse.java index 5e1ae5a02..807bdc489 100644 --- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/TenantResponse.java +++ b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/model/multitenancy/TenantResponse.java @@ -14,7 +14,10 @@ public class TenantResponse { private String tenantId; private String displayName; private String guid; - private String jdbcUrl; - private String user; + + private DatabaseConnection databaseConnection; + private SearchConnection searchConnection; + private AzureStorageConnection azureStorageConnection; + private S3StorageConnection s3StorageConnection; } diff --git a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/TenantsResource.java b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/TenantsResource.java index 39d18772d..b66581c84 100644 --- a/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/TenantsResource.java +++ b/persistence-service-v1/persistence-service-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/resources/TenantsResource.java @@ -29,6 +29,10 @@ public interface TenantsResource { List getTenants(); + @GetMapping(value = "/tenants" + TENANT_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE) + TenantResponse getTenant(@PathVariable(TENANT_ID_PARAM) String tenantId); + + @GetMapping(value = "/deploymentKey" + TENANT_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE) JSONPrimitive getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId); diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/AzureStorageConnectionEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/AzureStorageConnectionEntity.java new file mode 100644 index 000000000..f81b3a3e8 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/AzureStorageConnectionEntity.java @@ -0,0 +1,23 @@ +package com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity; + +import javax.persistence.Column; +import javax.persistence.Embeddable; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Embeddable +public class AzureStorageConnectionEntity { + + @Column(name = "storage_azure_connection_string") + private String connectionString; + @Column(name = "storage_azure_container_name") + private String containerName; + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/DatabaseConnectionEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/DatabaseConnectionEntity.java index 986a671cc..a5d8c5517 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/DatabaseConnectionEntity.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/DatabaseConnectionEntity.java @@ -1,27 +1,45 @@ package com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity; +import java.util.Map; + +import javax.persistence.Basic; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Embeddable; +import javax.persistence.FetchType; + +import com.iqser.red.service.persistence.management.v1.processor.utils.JSONMapConverter; import lombok.AllArgsConstructor; import lombok.Builder; +import lombok.Data; import lombok.NoArgsConstructor; +@Data @Builder @NoArgsConstructor @AllArgsConstructor @Embeddable public class DatabaseConnectionEntity { - @Column - private String jdbcDriver; - @Column - private String jdbcHost; - @Column - private String jdbcPort; - @Column - private String jdbcDatabase; - @Column - private String jdbcSchema; + @Column(name = "db_driver") + private String driver; + @Column(name = "db_host") + private String host; + @Column(name = "db_port") + private String port; + @Column(name = "db_database") + private String database; + @Column(name = "db_schema") + private String schema; + @Column(name = "db_username") + private String username; + @Column(name = "db_password") + private String password; + + @Basic(fetch = FetchType.EAGER) + @Column(columnDefinition = "text", name = "db_params") + @Convert(converter = JSONMapConverter.class) + private Map params; } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/S3StorageConnectionEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/S3StorageConnectionEntity.java new file mode 100644 index 000000000..bb95e8aab --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/S3StorageConnectionEntity.java @@ -0,0 +1,31 @@ +package com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity; + +import javax.persistence.Column; +import javax.persistence.Embeddable; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Embeddable +public class S3StorageConnectionEntity { + + @Column(name = "storage_s3_key") + private String key; + @Column(name = "storage_s3_secret") + private String secret; + @Column(name = "storage_s3_signer_type") + private String signerType; + @Column(name = "storage_s3_bucket_name") + private String bucketName; + @Column(name = "storage_s3_region") + private String region; + @Column(name = "storage_s3_endpoint") + private String endpoint; + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/SearchConnectionEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/SearchConnectionEntity.java new file mode 100644 index 000000000..c0fec1181 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/SearchConnectionEntity.java @@ -0,0 +1,40 @@ +package com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity; + +import java.util.Set; + +import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.Embeddable; + +import com.iqser.red.service.persistence.management.v1.processor.utils.JSONStringSetConverter; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Embeddable +public class SearchConnectionEntity { + + @Column(columnDefinition = "text", name = "search_hosts") + @Convert(converter = JSONStringSetConverter.class) + private Set hosts; + @Column(name = "search_port") + private int port = 9300; + @Column(name = "search_scheme") + private String scheme; + @Column(name = "search_username") + private String username; + @Column(name = "search_password") + private String password; + @Column(name = "search_number_of_shards") + private String numberOfShards; + @Column(name = "search_number_of_replicas") + private String numberOfReplicas; + + +} diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/TenantEntity.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/TenantEntity.java index 1a7b761e0..eb48cfc3e 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/TenantEntity.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/multitenancy/entity/TenantEntity.java @@ -25,12 +25,17 @@ public class TenantEntity { private String displayName; @Column private String guid; - @Column - private String username; - @Column - private String password; @Embedded private DatabaseConnectionEntity databaseConnection; + @Embedded + private SearchConnectionEntity searchConnection; + + @Embedded + private AzureStorageConnectionEntity azureStorageConnection; + + @Embedded + private S3StorageConnectionEntity s3StorageConnection; + } \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/DynamicDataSourceBasedMultiTenantConnectionProvider.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/DynamicDataSourceBasedMultiTenantConnectionProvider.java index 4be91740f..ced7a24bf 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/DynamicDataSourceBasedMultiTenantConnectionProvider.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/mulitenancy/DynamicDataSourceBasedMultiTenantConnectionProvider.java @@ -18,6 +18,7 @@ import com.google.common.cache.RemovalListener; import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.TenantEntity; import com.iqser.red.service.persistence.management.v1.processor.service.EncryptionDecryptionService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.iqser.red.service.persistence.management.v1.processor.utils.jdbc.JDBCUtils; import com.zaxxer.hikari.HikariDataSource; import lombok.RequiredArgsConstructor; @@ -85,13 +86,15 @@ public class DynamicDataSourceBasedMultiTenantConnectionProvider extends Abstrac private DataSource createAndConfigureDataSource(TenantEntity tenant) { - String decryptedPassword = encryptionService.decrypt(tenant.getPassword()); + String decryptedPassword = encryptionService.decrypt(tenant.getDatabaseConnection().getPassword()); HikariDataSource ds = masterDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); - ds.setUsername(tenant.getUsername()); + ds.setUsername(tenant.getDatabaseConnection().getUsername()); ds.setPassword(decryptedPassword); - ds.setJdbcUrl(tenant.getJdbcUrl()); + + // TODO Should not include schema + ds.setJdbcUrl(JDBCUtils.buildJdbcUrl(tenant.getDatabaseConnection())); ds.setPoolName(tenant.getTenantId() + TENANT_POOL_NAME_SUFFIX); @@ -99,4 +102,7 @@ public class DynamicDataSourceBasedMultiTenantConnectionProvider extends Abstrac return ds; } + + + } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/jdbc/JDBCUtils.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/jdbc/JDBCUtils.java new file mode 100644 index 000000000..6b7d3e978 --- /dev/null +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/utils/jdbc/JDBCUtils.java @@ -0,0 +1,48 @@ +package com.iqser.red.service.persistence.management.v1.processor.utils.jdbc; + +import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.DatabaseConnectionEntity; +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.DatabaseConnection; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class JDBCUtils { + + + public String buildJdbcUrl(DatabaseConnectionEntity databaseConnectionEntity){ + StringBuilder sb = new StringBuilder("jdbc:") + .append(databaseConnectionEntity.getDriver()) + .append("://") + .append(databaseConnectionEntity.getHost()) + .append(':') + .append(databaseConnectionEntity.getPort()) + .append('/') + .append(databaseConnectionEntity.getDatabase()) + .append('?') + .append("currentSchema=") + .append(databaseConnectionEntity.getSchema()); + if(databaseConnectionEntity.getParams() != null) { + databaseConnectionEntity.getParams().forEach((k, v) -> sb.append('&').append(k).append(v)); + } + return sb.toString(); + } + + + public String buildJdbcUrl(DatabaseConnection databaseConnection){ + StringBuilder sb = new StringBuilder("jdbc:") + .append(databaseConnection.getDriver()) + .append("://") + .append(databaseConnection.getHost()) + .append(':') + .append(databaseConnection.getPort()) + .append('/') + .append(databaseConnection.getDatabase()) + .append('?') + .append("currentSchema=") + .append(databaseConnection.getSchema()); + if(databaseConnection.getParams() != null) { + databaseConnection.getParams().forEach((k, v) -> sb.append('&').append(k).append(v)); + } + return sb.toString(); + } +} diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/controller/TenantsController.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/controller/TenantsController.java index 884f4b1b9..0610c6f34 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/controller/TenantsController.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/controller/TenantsController.java @@ -42,6 +42,12 @@ public class TenantsController implements TenantsResource { } + public TenantResponse getTenant(@PathVariable(TENANT_ID_PARAM) String tenantId) { + + return tenantManagementService.getTenant(tenantId); + } + + public JSONPrimitive getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId) { return JSONPrimitive.of(deploymentKeyService.getDeploymentKey(tenantId)); diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/TenantSpringLiquibaseExecutor.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/TenantSpringLiquibaseExecutor.java index 8de4d4bdb..c1bb68713 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/TenantSpringLiquibaseExecutor.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/multitenancy/persistence/TenantSpringLiquibaseExecutor.java @@ -16,6 +16,7 @@ import org.springframework.jdbc.datasource.SingleConnectionDataSource; import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.TenantEntity; import com.iqser.red.service.persistence.management.v1.processor.service.EncryptionDecryptionService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.iqser.red.service.persistence.management.v1.processor.utils.jdbc.JDBCUtils; import liquibase.integration.spring.SpringLiquibase; import lombok.RequiredArgsConstructor; @@ -51,7 +52,9 @@ public class TenantSpringLiquibaseExecutor implements InitializingBean, Resource for (TenantEntity tenant : tenants) { log.info("Initializing Liquibase for tenant " + tenant.getTenantId()); - try (Connection connection = DriverManager.getConnection(tenant.getJdbcUrl(), tenant.getUsername(), encryptionService.decrypt(tenant.getPassword()))) { + try (Connection connection = DriverManager.getConnection(JDBCUtils.buildJdbcUrl(tenant.getDatabaseConnection()), + tenant.getDatabaseConnection().getUsername(), + encryptionService.decrypt(tenant.getDatabaseConnection().getPassword()))) { DataSource tenantDataSource = new SingleConnectionDataSource(connection, false); SpringLiquibase liquibase = this.getSpringLiquibase(tenantDataSource); liquibase.afterPropertiesSet(); diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/TenantManagementService.java b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/TenantManagementService.java index d963c7001..b718bf956 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/TenantManagementService.java +++ b/persistence-service-v1/persistence-service-server-v1/src/main/java/com/iqser/red/service/peristence/v1/server/service/TenantManagementService.java @@ -22,9 +22,18 @@ import org.springframework.stereotype.Service; import com.iqser.red.service.peristence.v1.server.migration.AsyncMigrationStarterService; import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException; import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException; +import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.AzureStorageConnectionEntity; +import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.DatabaseConnectionEntity; +import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.S3StorageConnectionEntity; +import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.SearchConnectionEntity; import com.iqser.red.service.persistence.management.v1.processor.multitenancy.entity.TenantEntity; import com.iqser.red.service.persistence.management.v1.processor.service.EncryptionDecryptionService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.mulitenancy.repository.TenantRepository; +import com.iqser.red.service.persistence.management.v1.processor.utils.jdbc.JDBCUtils; +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.AzureStorageConnection; +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.DatabaseConnection; +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.S3StorageConnection; +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.SearchConnection; import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.TenantRequest; import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.TenantResponse; @@ -60,7 +69,8 @@ public class TenantManagementService { public TenantManagementService(EncryptionDecryptionService encryptionService, @Qualifier("tenantLiquibaseProperties") LiquibaseProperties liquibaseProperties, ResourceLoader resourceLoader, - TenantRepository tenantRepository, AsyncMigrationStarterService asyncMigrationStarterService) { + TenantRepository tenantRepository, + AsyncMigrationStarterService asyncMigrationStarterService) { this.encryptionService = encryptionService; this.liquibaseProperties = liquibaseProperties; @@ -75,11 +85,12 @@ public class TenantManagementService { if (tenantRepository.findById(tenantRequest.getTenantId()).isEmpty()) { - validateJdbcUrl(tenantRequest.getJdbcUrl()); + var jdbcUrl = JDBCUtils.buildJdbcUrl(tenantRequest.getDatabaseConnection()); + validateJdbcUrl(jdbcUrl); - String encryptedPassword = encryptionService.encrypt(tenantRequest.getPassword()); - - try (Connection connection = DriverManager.getConnection(tenantRequest.getJdbcUrl(), tenantRequest.getUser(), tenantRequest.getPassword())) { + try (Connection connection = DriverManager.getConnection(jdbcUrl, + tenantRequest.getDatabaseConnection().getUsername(), + tenantRequest.getDatabaseConnection().getPassword())) { DataSource tenantDataSource = new SingleConnectionDataSource(connection, false); runLiquibase(tenantDataSource); } catch (PSQLException e) { @@ -91,10 +102,44 @@ public class TenantManagementService { .tenantId(tenantRequest.getTenantId()) .displayName(tenantRequest.getDisplayName()) .guid(UUID.randomUUID().toString()) - .username(tenantRequest.getUser()) - .jdbcUrl(tenantRequest.getJdbcUrl()) - .password(encryptedPassword) + .databaseConnection(DatabaseConnectionEntity.builder() + .driver(tenantRequest.getDatabaseConnection().getDriver()) + .host(tenantRequest.getDatabaseConnection().getHost()) + .port(tenantRequest.getDatabaseConnection().getPort()) + .database(tenantRequest.getDatabaseConnection().getDatabase()) + .schema(tenantRequest.getDatabaseConnection().getSchema()) + .username(tenantRequest.getDatabaseConnection().getUsername()) + .password(encryptionService.encrypt(tenantRequest.getDatabaseConnection().getPassword())) + .build()) + .searchConnection(SearchConnectionEntity.builder() + .hosts(tenantRequest.getSearchConnection().getHosts()) + .port(tenantRequest.getSearchConnection().getPort()) + .scheme(tenantRequest.getSearchConnection().getScheme()) + .username(tenantRequest.getSearchConnection().getUsername()) + .password(encryptionService.encrypt(tenantRequest.getSearchConnection().getPassword())) + .numberOfShards(tenantRequest.getSearchConnection().getNumberOfShards()) + .numberOfReplicas(tenantRequest.getSearchConnection().getNumberOfReplicas()) + .build()) .build(); + + if (tenantRequest.getAzureStorageConnection() != null) { + tenantEntity.setAzureStorageConnection(AzureStorageConnectionEntity.builder() + .connectionString(encryptionService.encrypt(tenantRequest.getAzureStorageConnection().getConnectionString())) + .containerName(tenantRequest.getAzureStorageConnection().getContainerName()) + .build()); + } + + if (tenantRequest.getS3StorageConnection() != null) { + tenantEntity.setS3StorageConnection(S3StorageConnectionEntity.builder() + .key(tenantRequest.getS3StorageConnection().getKey()) + .secret(encryptionService.encrypt(tenantRequest.getS3StorageConnection().getSecret())) + .signerType(tenantRequest.getS3StorageConnection().getSignerType()) + .bucketName(tenantRequest.getS3StorageConnection().getBucketName()) + .region(tenantRequest.getS3StorageConnection().getRegion()) + .endpoint(tenantRequest.getS3StorageConnection().getEndpoint()) + .build()); + } + tenantRepository.save(tenantEntity); asyncMigrationStarterService.runForTenant(tenantRequest.getTenantId()); @@ -156,13 +201,50 @@ public class TenantManagementService { private TenantResponse convert(TenantEntity entity) { - return TenantResponse.builder() + var tenantResponse = TenantResponse.builder() .tenantId(entity.getTenantId()) .displayName(entity.getDisplayName()) .guid(entity.getGuid()) - .jdbcUrl(entity.getJdbcUrl()) - .user(entity.getUsername()) + .databaseConnection(DatabaseConnection.builder() + .driver(entity.getDatabaseConnection().getDriver()) + .host(entity.getDatabaseConnection().getHost()) + .port(entity.getDatabaseConnection().getPort()) + .database(entity.getDatabaseConnection().getDatabase()) + .schema(entity.getDatabaseConnection().getSchema()) + .username(entity.getDatabaseConnection().getUsername()) + .password(entity.getDatabaseConnection().getPassword()) + .params(entity.getDatabaseConnection().getParams()) + .build()) + .searchConnection(SearchConnection.builder() + .hosts(entity.getSearchConnection().getHosts()) + .port(entity.getSearchConnection().getPort()) + .scheme(entity.getSearchConnection().getScheme()) + .username(entity.getSearchConnection().getUsername()) + .password(entity.getSearchConnection().getPassword()) + .numberOfShards(entity.getSearchConnection().getNumberOfShards()) + .numberOfReplicas(entity.getSearchConnection().getNumberOfReplicas()) + .build()) .build(); + + if (entity.getAzureStorageConnection() != null) { + tenantResponse.setAzureStorageConnection(AzureStorageConnection.builder() + .connectionString(entity.getAzureStorageConnection().getConnectionString()) + .containerName(entity.getAzureStorageConnection().getContainerName()) + .build()); + } + + if (entity.getS3StorageConnection() != null) { + tenantResponse.setS3StorageConnection(S3StorageConnection.builder() + .key(entity.getS3StorageConnection().getKey()) + .secret(entity.getS3StorageConnection().getSecret()) + .signerType(entity.getS3StorageConnection().getSignerType()) + .bucketName(entity.getS3StorageConnection().getBucketName()) + .region(entity.getS3StorageConnection().getRegion()) + .endpoint(entity.getS3StorageConnection().getEndpoint()) + .build()); + } + + return tenantResponse; } diff --git a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/master/3-detailed-db-connection.changelog.yaml b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/master/3-detailed-db-connection.changelog.yaml index 2f51f0fa7..e414d353b 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/master/3-detailed-db-connection.changelog.yaml +++ b/persistence-service-v1/persistence-service-server-v1/src/main/resources/db/changelog/master/3-detailed-db-connection.changelog.yaml @@ -1,7 +1,7 @@ databaseChangeLog: - changeSet: id: detailed-db-connection - author: viktorseifert + author: dom changes: - deleteColumn: columns: @@ -11,33 +11,72 @@ databaseChangeLog: - addColumn: columns: - column: - name: jdbc_driver + name: db_driver type: VARCHAR(255) - constraints: - unique: true - uniqueConstraintName: UC_TENANT_CONNECTION - column: - name: jdbc_host + name: db_host type: VARCHAR(255) - constraints: - unique: true - uniqueConstraintName: UC_TENANT_CONNECTION - column: - name: jdbc_port + name: db_port type: VARCHAR(255) - constraints: - unique: true - uniqueConstraintName: UC_TENANT_CONNECTION - column: - name: jdbc_database + name: db_database type: VARCHAR(255) - constraints: - unique: true - uniqueConstraintName: UC_TENANT_CONNECTION - column: - name: jdbc_schema + name: db_schema + type: VARCHAR(255) + - column: + name: db_params + type: VARCHAR(255) + - column: + name: db_username + type: VARCHAR(255) + - column: + name: db_password + type: VARCHAR(255) + - column: + name: search_hosts + type: VARCHAR(255) + - column: + name: search_port + type: VARCHAR(255) + - column: + name: search_scheme + type: VARCHAR(255) + - column: + name: search_username + type: VARCHAR(255) + - column: + name: search_password + type: VARCHAR(255) + - column: + name: search_number_of_shards + type: VARCHAR(255) + - column: + name: search_number_of_replicas + type: VARCHAR(255) + - column: + name: storage_azure_connection_string + type: VARCHAR(255) + - column: + name: storage_azure_container_name + type: VARCHAR(255) + - column: + name: storage_s3_key + type: VARCHAR(255) + - column: + name: storage_s3_secret + type: VARCHAR(255) + - column: + name: storage_s3_signer_type + type: VARCHAR(255) + - column: + name: storage_s3_bucket_name + type: VARCHAR(255) + - column: + name: storage_s3_region + type: VARCHAR(255) + - column: + name: storage_s3_endpoint type: VARCHAR(255) - constraints: - unique: true - uniqueConstraintName: UC_TENANT_CONNECTION tableName: tenant \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java index 3f2a62343..377ed927f 100644 --- a/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java +++ b/persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/utils/AbstractPersistenceServerServiceTest.java @@ -5,6 +5,7 @@ import static org.mockito.Mockito.when; import java.sql.Connection; import java.sql.DriverManager; +import java.util.Set; import java.util.UUID; import javax.sql.DataSource; @@ -78,6 +79,9 @@ import com.iqser.red.service.persistence.management.v1.processor.service.persist import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.WatermarkRepository; import com.iqser.red.service.persistence.management.v1.processor.utils.multitenancy.TenantContext; import com.iqser.red.service.persistence.service.v1.api.model.dossiertemplate.configuration.ApplicationConfig; +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.DatabaseConnection; +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.S3StorageConnection; +import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.SearchConnection; import com.iqser.red.service.persistence.service.v1.api.model.multitenancy.TenantRequest; import com.iqser.red.service.redaction.v1.model.RedactionLog; import com.iqser.red.service.redaction.v1.model.RedactionResult; @@ -182,7 +186,6 @@ public abstract class AbstractPersistenceServerServiceTest { protected PrometheusMeterRegistry prometheusMeterRegistry; - @BeforeEach public void setupOptimize() { @@ -215,11 +218,44 @@ public abstract class AbstractPersistenceServerServiceTest { var postgreSQLContainerMaster = SpringPostgreSQLTestContainer.getInstance().withDatabaseName("integration-tests-db-master").withUsername("sa").withPassword("sa"); var jdbcUrl = postgreSQLContainerMaster.getJdbcUrl().substring(0, postgreSQLContainerMaster.getJdbcUrl().lastIndexOf('/') + 1) + "redaction?currentSchema=myschema"; + var port = postgreSQLContainerMaster.getJdbcUrl().substring(0, postgreSQLContainerMaster.getJdbcUrl().lastIndexOf('/')).split(":")[3]; createDatabase("redaction", "redaction"); createSchema(jdbcUrl, "redaction", "redaction"); - tenantsClient.createTenant(new TenantRequest("redaction", "Redaction default", UUID.randomUUID().toString(), jdbcUrl, "redaction", "redaction")); + var tenantRequest = TenantRequest.builder() + .tenantId("redaction") + .displayName("Redaction default") + .guid(UUID.randomUUID().toString()) + .databaseConnection(DatabaseConnection.builder() + .driver("postgresql") + .host("localhost") + .port(port) + .database("redaction") + .schema("myschema") + .username("redaction") + .password("redaction") + .build()) + .searchConnection(SearchConnection.builder() + .hosts(Set.of("elasticsearchHost")) + .port(9200) + .scheme("https") + .username("elastic") + .password("changeMe") + .numberOfShards("1") + .numberOfReplicas("5") + .build()) + .s3StorageConnection(S3StorageConnection.builder() + .key("key") + .secret("secret") + .signerType("signerType") + .bucketName("bucketName") + .region("eu") + .endpoint("endpoint") + .build()) + .build(); + + tenantsClient.createTenant(tenantRequest); } }