From 8783dc91e79d83813a242fb60697c91c78c09db1 Mon Sep 17 00:00:00 2001 From: yhampe Date: Tue, 23 Jan 2024 12:51:29 +0100 Subject: [PATCH 1/7] RED-6888: Add delete tenant endpoint added endpoint added methods to service deleting everything except cache --- .../api/external/TenantsResource.java | 9 ++ .../external/TenantsController.java | 14 +++ .../events/TenantDeletedEvent.java | 17 ++++ .../messaging/MessagingConfiguration.java | 6 ++ .../UserManagementPermissions.java | 2 + .../service/TenantManagementService.java | 92 +++++++++++++++++++ 6 files changed, 140 insertions(+) create mode 100644 src/main/java/com/knecon/fforesight/tenantusermanagement/events/TenantDeletedEvent.java diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java index 5defd4a..fd87d4f 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java @@ -4,11 +4,13 @@ import java.util.List; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import com.fasterxml.jackson.databind.JsonNode; @@ -33,6 +35,13 @@ public interface TenantsResource { @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")}) void createTenant(@RequestBody TenantRequest tenant); + @ResponseBody + @ResponseStatus(value = HttpStatus.NO_CONTENT) + @Operation(summary = "Deletes given tenant", description = "None") + @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "405", description = "Operation is not allowed."), @ApiResponse(responseCode = "409", description = "Conflict while deleting user.")}) + @DeleteMapping(value = "/tenants/{tenantId}") + void deleteTenant(@PathVariable("tenantId") String tenantId, @RequestBody TenantRequest tenant); + @GetMapping(value = "/tenants", produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Gets all existing tenants", description = "None") diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java index be1fde2..cd45848 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java @@ -1,6 +1,7 @@ package com.knecon.fforesight.tenantusermanagement.controller.external; import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.CREATE_TENANT; +import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.DELETE_TENANT; import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.DEPLOYMENT_INFO; import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.GET_TENANTS; import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.UPDATE_TENANT; @@ -8,6 +9,8 @@ import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagem import java.util.List; import java.util.stream.Collectors; +import javax.ws.rs.GET; + import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; @@ -46,6 +49,17 @@ public class TenantsController implements TenantsResource, PublicResource { } } + @PreAuthorize("hasAuthority('"+ DELETE_TENANT+ "')") + public void deleteTenant(String tenantId, TenantRequest tenantRequest) { + + try { + tenantManagementService.deleteTenant(tenantId, tenantRequest); + } catch (IllegalArgumentException e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e); + } + + } + @PreAuthorize("hasAuthority('" + GET_TENANTS + "')") public List getTenants() { diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/events/TenantDeletedEvent.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/events/TenantDeletedEvent.java new file mode 100644 index 0000000..77c2b18 --- /dev/null +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/events/TenantDeletedEvent.java @@ -0,0 +1,17 @@ +package com.knecon.fforesight.tenantusermanagement.events; + +import com.knecon.fforesight.tenantcommons.model.SearchConnection; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data +@AllArgsConstructor +public class TenantDeletedEvent { + private String tenantId; + + private SearchConnection searchConnection; + +} diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/messaging/MessagingConfiguration.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/messaging/MessagingConfiguration.java index c493820..9367f9d 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/messaging/MessagingConfiguration.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/messaging/MessagingConfiguration.java @@ -20,6 +20,12 @@ public class MessagingConfiguration { return new TopicExchange(tenantExchangeName); } + @Bean + TopicExchange deleteTenantExchange(@Value("${fforesight.tenant-exchange.name}") String tenantExchangeName) { + + return new TopicExchange(tenantExchangeName); + } + @Bean TopicExchange userExchange(@Value("${fforesight.user-exchange.name}") String userExchangeName) { diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/permissions/UserManagementPermissions.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/permissions/UserManagementPermissions.java index 7d02967..ef41447 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/permissions/UserManagementPermissions.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/permissions/UserManagementPermissions.java @@ -19,6 +19,8 @@ public class UserManagementPermissions { public static final String CREATE_TENANT = "fforesight-create-tenant"; public static final String GET_TENANTS = "fforesight-get-tenants"; public static final String UPDATE_TENANT = "fforesight-update-tenant"; + + public static final String DELETE_TENANT="fforesight-delete-tenant"; public static final String DEPLOYMENT_INFO = "fforesight-deployment-info"; // SMTP diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java index 943cb44..e59ffca 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java @@ -14,6 +14,7 @@ import java.util.UUID; import java.util.stream.Collectors; import javax.sql.DataSource; +import javax.ws.rs.BadRequestException; import javax.ws.rs.NotFoundException; import org.apache.commons.lang3.StringUtils; @@ -33,6 +34,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; import org.springframework.web.server.ResponseStatusException; +import com.azure.storage.blob.BlobServiceClient; import com.fasterxml.jackson.databind.JsonNode; import com.knecon.fforesight.tenantcommons.EncryptionDecryptionService; import com.knecon.fforesight.tenantcommons.TenantContext; @@ -49,6 +51,7 @@ import com.knecon.fforesight.tenantusermanagement.entity.S3StorageConnectionEnti import com.knecon.fforesight.tenantusermanagement.entity.SearchConnectionEntity; import com.knecon.fforesight.tenantusermanagement.entity.TenantEntity; import com.knecon.fforesight.tenantusermanagement.events.TenantCreatedEvent; +import com.knecon.fforesight.tenantusermanagement.events.TenantDeletedEvent; import com.knecon.fforesight.tenantusermanagement.events.TenantSyncEvent; import com.knecon.fforesight.tenantusermanagement.model.TenantRequest; import com.knecon.fforesight.tenantusermanagement.model.TenantUser; @@ -59,6 +62,7 @@ import com.knecon.fforesight.tenantusermanagement.utils.JDBCUtils; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; @Slf4j @Service @@ -182,6 +186,64 @@ public class TenantManagementService implements TenantProvider { } } + @SneakyThrows + public TenantResponse deleteTenant(String tenantId, TenantRequest tenantRequest) { + + log.info("Requested to delete tenant for: {}", tenantRequest.getTenantId()); + + if (tenantRequest.getS3StorageConnection() != null && tenantRequest.getAzureStorageConnection() != null) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Not possible to set both azure and s3 connection, please only specify one"); + } + + var tenant = tenantRepository.findByTenantId(tenantId); + + + if (tenant.isPresent()) { + + log.info("Deleting tenant: {}", tenantRequest.getTenantId()); + var tenantEntity = tenant.get(); + + log.info("Deleting search index for tenant: {}", tenantRequest.getTenantId()); + TenantContext.setTenantId(tenantEntity.getTenantId()); + rabbitTemplate.convertAndSend(tenantExchangeName, "tenant.deleted", new TenantDeletedEvent(tenantEntity.getTenantId(),SearchConnection.builder() + .hosts(tenantEntity.getSearchConnection().getHosts()) + .port(tenantEntity.getSearchConnection().getPort()) + .scheme(tenantEntity.getSearchConnection().getScheme()) + .username(tenantEntity.getSearchConnection().getUsername()) + .numberOfShards(tenantEntity.getSearchConnection().getNumberOfShards()) + .numberOfReplicas(tenantEntity.getSearchConnection().getNumberOfReplicas()) + .password(tenantEntity.getSearchConnection().getPassword()) + .indexPrefix(tenantEntity.getSearchConnection().getIndexPrefix()) + .build())); + TenantContext.clear(); + + log.info("Dispatched delete index message for tenant: {}", tenantRequest.getTenantId()); + //todo: delete cache + deleteSchema(tenantRequest); + com.iqser.red.storage.commons.model.AzureStorageConnection azureStorageConnection = new com.iqser.red.storage.commons.model.AzureStorageConnection(tenantRequest.getAzureStorageConnection().getConnectionString(), tenantRequest.getAzureStorageConnection().getContainerName()); + if (azureStorageConnection != null) { + log.info("Deleting azure blob for tenant: {}",tenantId); + BlobServiceClient blobServiceClient = storageConfiguration.getAzureBlobStorageService().initBlobServiceClient(azureStorageConnection); + blobServiceClient.deleteBlobContainer(tenantRequest.getAzureStorageConnection().getContainerName()); + } + var s3StorageConnectionTemplate = tenantRequest.getS3StorageConnection(); + com.iqser.red.storage.commons.model.S3StorageConnection s3StorageConnection = new com.iqser.red.storage.commons.model.S3StorageConnection(s3StorageConnectionTemplate.getKey(), s3StorageConnectionTemplate.getSecret(), s3StorageConnectionTemplate.getSignerType(), s3StorageConnectionTemplate.getBucketName(), s3StorageConnectionTemplate.getRegion(),s3StorageConnectionTemplate.getEndpoint()); + if (s3StorageConnection != null) { + log.info("Deleting s3 bucket for tenant: {}",tenantId); + DeleteBucketRequest deleteBucketRequest = DeleteBucketRequest.builder() + .bucket(s3StorageConnection.getBucketName()) + .expectedBucketOwner(tenantId) + .build(); + storageConfiguration.getS3StorageService().initAmazonS3(s3StorageConnection).deleteBucket(deleteBucketRequest); + } + deleteRealm(tenantId); + return convert(tenantRepository.delete(tenantEntity)); + + } else { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Tenant does not exist"); + } + } + private String buildIndexPrefix(String tenantId) { @@ -256,6 +318,22 @@ public class TenantManagementService implements TenantProvider { } } + private void deleteSchema(TenantRequest tenantRequest) { + log.info("Deleting schema for tenant: {}", tenantRequest.getTenantId()); + var jdbcUrl = JDBCUtils.buildJdbcUrl(tenantRequest.getDatabaseConnection()); + try (Connection connection = DriverManager.getConnection(jdbcUrl, + tenantRequest.getDatabaseConnection().getUsername(), + tenantRequest.getDatabaseConnection().getPassword())) { + DataSource tenantDataSource = new SingleConnectionDataSource(connection, false); + JdbcTemplate jdbcTemplate = new JdbcTemplate(tenantDataSource); + String deleteStatement = "DELETE SCHEMA IF EXISTS \"" + tenantRequest.getDatabaseConnection().getSchema() + "\""; + jdbcTemplate.execute(deleteStatement); + } catch (Exception e) { + log.warn("Could not delete schema:", e); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Tenant deletion failed: " + e.getMessage(), e); + } + } + public void createOrUpdateRealm(String tenantId, List users) { @@ -281,6 +359,20 @@ public class TenantManagementService implements TenantProvider { keycloak.getAdminClient().realms().create(realm); } + public void deleteRealm(String tenantId) { + + try { + log.info("Deleting existing realms for tenant: {}", tenantId); + keycloak.getAdminClient().realm(tenantId).remove(); + } + + catch (Exception e) { + log.warn("Could not delete realm:", e); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Tenant deletion failed: " + e.getMessage(), e); + } + + } + private boolean syncRealmIfExists(String tenantId) { From 96d03f756d7eb24956558e76d7f72241e3517d6d Mon Sep 17 00:00:00 2001 From: yhampe Date: Tue, 23 Jan 2024 14:58:04 +0100 Subject: [PATCH 2/7] RED-6888: Add delete tenant endpoint changed delete tenant queue name --- .../tenantusermanagement/service/TenantManagementService.java | 3 +++ src/main/resources/application.yml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java index e59ffca..b10683a 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java @@ -89,6 +89,9 @@ public class TenantManagementService implements TenantProvider { @Value("${fforesight.tenant-exchange.name}") private String tenantExchangeName; + @Value("${fforesight.tenant-delete.name}") + private String tenantDeleteName; + @SneakyThrows public TenantResponse createTenant(TenantRequest tenantRequest) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 655c803..21a6499 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -110,6 +110,8 @@ fforesight: app-prefix: 'fforesight' tenant-exchange: name: 'tenants-exchange' + tenant-delete: + name: 'tenant-delete-queue' user-exchange: name: 'users-exchange' springdoc: From 64cecc4a7d42cef52f392b3da8ee3a91b53f23b7 Mon Sep 17 00:00:00 2001 From: yhampe Date: Wed, 24 Jan 2024 09:45:46 +0100 Subject: [PATCH 3/7] RED-6888: Add delete tenant endpoint changed method signature to void --- .../service/TenantManagementService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java index b10683a..dfd23a5 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java @@ -190,7 +190,7 @@ public class TenantManagementService implements TenantProvider { } @SneakyThrows - public TenantResponse deleteTenant(String tenantId, TenantRequest tenantRequest) { + public void deleteTenant(String tenantId, TenantRequest tenantRequest) { log.info("Requested to delete tenant for: {}", tenantRequest.getTenantId()); @@ -208,7 +208,7 @@ public class TenantManagementService implements TenantProvider { log.info("Deleting search index for tenant: {}", tenantRequest.getTenantId()); TenantContext.setTenantId(tenantEntity.getTenantId()); - rabbitTemplate.convertAndSend(tenantExchangeName, "tenant.deleted", new TenantDeletedEvent(tenantEntity.getTenantId(),SearchConnection.builder() + rabbitTemplate.convertAndSend(tenantDeleteName, "tenant.deleted", new TenantDeletedEvent(tenantEntity.getTenantId(),SearchConnection.builder() .hosts(tenantEntity.getSearchConnection().getHosts()) .port(tenantEntity.getSearchConnection().getPort()) .scheme(tenantEntity.getSearchConnection().getScheme()) @@ -240,7 +240,7 @@ public class TenantManagementService implements TenantProvider { storageConfiguration.getS3StorageService().initAmazonS3(s3StorageConnection).deleteBucket(deleteBucketRequest); } deleteRealm(tenantId); - return convert(tenantRepository.delete(tenantEntity)); + tenantRepository.delete(tenantEntity); } else { throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Tenant does not exist"); From 819730d97062a61618abb4dcdbcbfa22b92ae752 Mon Sep 17 00:00:00 2001 From: yhampe Date: Thu, 25 Jan 2024 09:49:48 +0100 Subject: [PATCH 4/7] RED-6888: Add delete tenant endpoint removed extra topic exchange removed not needed event fixed code errors tested on stack --- .../api/external/TenantsResource.java | 2 +- .../external/TenantsController.java | 5 +- .../events/TenantDeletedEvent.java | 17 --- .../messaging/MessagingConfiguration.java | 7 - .../service/TenantManagementService.java | 123 +++++++++--------- src/main/resources/application.yml | 2 - 6 files changed, 68 insertions(+), 88 deletions(-) delete mode 100644 src/main/java/com/knecon/fforesight/tenantusermanagement/events/TenantDeletedEvent.java diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java index fd87d4f..6919be7 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java @@ -40,7 +40,7 @@ public interface TenantsResource { @Operation(summary = "Deletes given tenant", description = "None") @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "405", description = "Operation is not allowed."), @ApiResponse(responseCode = "409", description = "Conflict while deleting user.")}) @DeleteMapping(value = "/tenants/{tenantId}") - void deleteTenant(@PathVariable("tenantId") String tenantId, @RequestBody TenantRequest tenant); + void deleteTenant(@PathVariable("tenantId") String tenantId); @GetMapping(value = "/tenants", produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java index cd45848..07e9f22 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java @@ -49,11 +49,10 @@ public class TenantsController implements TenantsResource, PublicResource { } } - @PreAuthorize("hasAuthority('"+ DELETE_TENANT+ "')") - public void deleteTenant(String tenantId, TenantRequest tenantRequest) { + public void deleteTenant(String tenantId) { try { - tenantManagementService.deleteTenant(tenantId, tenantRequest); + tenantManagementService.deleteTenant(tenantId); } catch (IllegalArgumentException e) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e); } diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/events/TenantDeletedEvent.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/events/TenantDeletedEvent.java deleted file mode 100644 index 77c2b18..0000000 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/events/TenantDeletedEvent.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.knecon.fforesight.tenantusermanagement.events; - -import com.knecon.fforesight.tenantcommons.model.SearchConnection; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@NoArgsConstructor -@Data -@AllArgsConstructor -public class TenantDeletedEvent { - private String tenantId; - - private SearchConnection searchConnection; - -} diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/messaging/MessagingConfiguration.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/messaging/MessagingConfiguration.java index 9367f9d..f5b23ce 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/messaging/MessagingConfiguration.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/messaging/MessagingConfiguration.java @@ -20,13 +20,6 @@ public class MessagingConfiguration { return new TopicExchange(tenantExchangeName); } - @Bean - TopicExchange deleteTenantExchange(@Value("${fforesight.tenant-exchange.name}") String tenantExchangeName) { - - return new TopicExchange(tenantExchangeName); - } - - @Bean TopicExchange userExchange(@Value("${fforesight.user-exchange.name}") String userExchangeName) { diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java index dfd23a5..3736347 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java @@ -14,7 +14,6 @@ import java.util.UUID; import java.util.stream.Collectors; import javax.sql.DataSource; -import javax.ws.rs.BadRequestException; import javax.ws.rs.NotFoundException; import org.apache.commons.lang3.StringUtils; @@ -34,7 +33,11 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; import org.springframework.web.server.ResponseStatusException; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobServiceClient; +import com.azure.storage.blob.BlobServiceClientBuilder; +import com.azure.storage.blob.models.BlobItem; import com.fasterxml.jackson.databind.JsonNode; import com.knecon.fforesight.tenantcommons.EncryptionDecryptionService; import com.knecon.fforesight.tenantcommons.TenantContext; @@ -51,7 +54,6 @@ import com.knecon.fforesight.tenantusermanagement.entity.S3StorageConnectionEnti import com.knecon.fforesight.tenantusermanagement.entity.SearchConnectionEntity; import com.knecon.fforesight.tenantusermanagement.entity.TenantEntity; import com.knecon.fforesight.tenantusermanagement.events.TenantCreatedEvent; -import com.knecon.fforesight.tenantusermanagement.events.TenantDeletedEvent; import com.knecon.fforesight.tenantusermanagement.events.TenantSyncEvent; import com.knecon.fforesight.tenantusermanagement.model.TenantRequest; import com.knecon.fforesight.tenantusermanagement.model.TenantUser; @@ -62,7 +64,12 @@ import com.knecon.fforesight.tenantusermanagement.utils.JDBCUtils; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsResponse; +import software.amazon.awssdk.services.s3.model.S3Object; @Slf4j @Service @@ -89,10 +96,6 @@ public class TenantManagementService implements TenantProvider { @Value("${fforesight.tenant-exchange.name}") private String tenantExchangeName; - @Value("${fforesight.tenant-delete.name}") - private String tenantDeleteName; - - @SneakyThrows public TenantResponse createTenant(TenantRequest tenantRequest) { @@ -190,61 +193,63 @@ public class TenantManagementService implements TenantProvider { } @SneakyThrows - public void deleteTenant(String tenantId, TenantRequest tenantRequest) { + public void deleteTenant(String tenantId) { - log.info("Requested to delete tenant for: {}", tenantRequest.getTenantId()); + TenantResponse tenant = this.getTenant(tenantId); + log.info("Requested to delete tenant for: {}", tenant.getTenantId()); - if (tenantRequest.getS3StorageConnection() != null && tenantRequest.getAzureStorageConnection() != null) { + if (tenant.getS3StorageConnection() != null && tenant.getAzureStorageConnection() != null) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Not possible to set both azure and s3 connection, please only specify one"); } - var tenant = tenantRepository.findByTenantId(tenantId); + log.info("Deleting tenant: {}", tenant.getTenantId()); + log.info("Deleting search index for tenant: {}", tenant.getTenantId()); + TenantContext.setTenantId(tenant.getTenantId()); + rabbitTemplate.convertAndSend(tenantExchangeName, "tenant.deleted", tenant); + TenantContext.clear(); + log.info("Dispatched delete index message for tenant: {}", tenant.getTenantId()); + deleteSchema(tenant); + if(tenant.getAzureStorageConnection() != null ) { + log.info("Deleting azure blob for tenant: {}",tenantId); + String connectionString = tenant.getAzureStorageConnection().getConnectionString(); + String containerName = tenant.getAzureStorageConnection().getContainerName(); - if (tenant.isPresent()) { + BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(connectionString).buildClient(); + BlobContainerClient containerClient = blobServiceClient.getBlobContainerClient(containerName); - log.info("Deleting tenant: {}", tenantRequest.getTenantId()); - var tenantEntity = tenant.get(); - - log.info("Deleting search index for tenant: {}", tenantRequest.getTenantId()); - TenantContext.setTenantId(tenantEntity.getTenantId()); - rabbitTemplate.convertAndSend(tenantDeleteName, "tenant.deleted", new TenantDeletedEvent(tenantEntity.getTenantId(),SearchConnection.builder() - .hosts(tenantEntity.getSearchConnection().getHosts()) - .port(tenantEntity.getSearchConnection().getPort()) - .scheme(tenantEntity.getSearchConnection().getScheme()) - .username(tenantEntity.getSearchConnection().getUsername()) - .numberOfShards(tenantEntity.getSearchConnection().getNumberOfShards()) - .numberOfReplicas(tenantEntity.getSearchConnection().getNumberOfReplicas()) - .password(tenantEntity.getSearchConnection().getPassword()) - .indexPrefix(tenantEntity.getSearchConnection().getIndexPrefix()) - .build())); - TenantContext.clear(); - - log.info("Dispatched delete index message for tenant: {}", tenantRequest.getTenantId()); - //todo: delete cache - deleteSchema(tenantRequest); - com.iqser.red.storage.commons.model.AzureStorageConnection azureStorageConnection = new com.iqser.red.storage.commons.model.AzureStorageConnection(tenantRequest.getAzureStorageConnection().getConnectionString(), tenantRequest.getAzureStorageConnection().getContainerName()); - if (azureStorageConnection != null) { - log.info("Deleting azure blob for tenant: {}",tenantId); - BlobServiceClient blobServiceClient = storageConfiguration.getAzureBlobStorageService().initBlobServiceClient(azureStorageConnection); - blobServiceClient.deleteBlobContainer(tenantRequest.getAzureStorageConnection().getContainerName()); + // Delete all blobs within the container + for (BlobItem blobItem : containerClient.listBlobs()) { + BlobClient blobClient = containerClient.getBlobClient(blobItem.getName()); + blobClient.delete(); } - var s3StorageConnectionTemplate = tenantRequest.getS3StorageConnection(); - com.iqser.red.storage.commons.model.S3StorageConnection s3StorageConnection = new com.iqser.red.storage.commons.model.S3StorageConnection(s3StorageConnectionTemplate.getKey(), s3StorageConnectionTemplate.getSecret(), s3StorageConnectionTemplate.getSignerType(), s3StorageConnectionTemplate.getBucketName(), s3StorageConnectionTemplate.getRegion(),s3StorageConnectionTemplate.getEndpoint()); - if (s3StorageConnection != null) { - log.info("Deleting s3 bucket for tenant: {}",tenantId); - DeleteBucketRequest deleteBucketRequest = DeleteBucketRequest.builder() - .bucket(s3StorageConnection.getBucketName()) - .expectedBucketOwner(tenantId) - .build(); - storageConfiguration.getS3StorageService().initAmazonS3(s3StorageConnection).deleteBucket(deleteBucketRequest); - } - deleteRealm(tenantId); - tenantRepository.delete(tenantEntity); - - } else { - throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Tenant does not exist"); + containerClient.delete(); } + + if(tenant.getS3StorageConnection() != null) { + var s3StorageConnectionTemplate = tenant.getS3StorageConnection(); + com.iqser.red.storage.commons.model.S3StorageConnection s3StorageConnection = new com.iqser.red.storage.commons.model.S3StorageConnection(s3StorageConnectionTemplate.getKey(), encryptionService.decrypt(s3StorageConnectionTemplate.getSecret()), s3StorageConnectionTemplate.getSignerType(), s3StorageConnectionTemplate.getBucketName(), s3StorageConnectionTemplate.getRegion(),s3StorageConnectionTemplate.getEndpoint()); + log.info("Deleting s3 bucket for tenant: {}",tenantId); + S3Client client = storageConfiguration.getS3StorageService().initAmazonS3(s3StorageConnection); + String bucketName = s3StorageConnection.getBucketName(); + ListObjectsRequest listObjects = ListObjectsRequest + .builder() + .bucket(bucketName) + .build(); + ListObjectsResponse objectList = client.listObjects(listObjects); + for (S3Object object : objectList.contents()) { + // Delete each object + client.deleteObject(DeleteObjectRequest.builder().bucket(bucketName).key(object.key()).build()); + } + DeleteBucketRequest deleteBucketRequest = DeleteBucketRequest.builder() + .bucket(bucketName) + .expectedBucketOwner(tenantId) + .build(); + client.deleteBucket(deleteBucketRequest); + } + deleteRealm(tenantId); + tenantRepository.deleteById(tenant.getTenantId()); + } @@ -321,16 +326,18 @@ public class TenantManagementService implements TenantProvider { } } - private void deleteSchema(TenantRequest tenantRequest) { - log.info("Deleting schema for tenant: {}", tenantRequest.getTenantId()); - var jdbcUrl = JDBCUtils.buildJdbcUrl(tenantRequest.getDatabaseConnection()); + + private void deleteSchema(TenantResponse tenant) { + log.info("Deleting schema for tenant: {}", tenant.getTenantId()); + var jdbcUrl = JDBCUtils.buildJdbcUrl(tenant.getDatabaseConnection()); try (Connection connection = DriverManager.getConnection(jdbcUrl, - tenantRequest.getDatabaseConnection().getUsername(), - tenantRequest.getDatabaseConnection().getPassword())) { + tenant.getDatabaseConnection().getUsername(), + this.encryptionService.decrypt(tenant.getDatabaseConnection().getPassword()))) + { DataSource tenantDataSource = new SingleConnectionDataSource(connection, false); JdbcTemplate jdbcTemplate = new JdbcTemplate(tenantDataSource); - String deleteStatement = "DELETE SCHEMA IF EXISTS \"" + tenantRequest.getDatabaseConnection().getSchema() + "\""; - jdbcTemplate.execute(deleteStatement); + String deleteStatement = "DROP SCHEMA IF EXISTS \"" + tenant.getDatabaseConnection().getSchema() + "\" CASCADE;"; + jdbcTemplate.execute(deleteStatement); } catch (Exception e) { log.warn("Could not delete schema:", e); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Tenant deletion failed: " + e.getMessage(), e); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 21a6499..655c803 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -110,8 +110,6 @@ fforesight: app-prefix: 'fforesight' tenant-exchange: name: 'tenants-exchange' - tenant-delete: - name: 'tenant-delete-queue' user-exchange: name: 'users-exchange' springdoc: From 51d10398667b947c514f2e9f9ecac2087ac20a1f Mon Sep 17 00:00:00 2001 From: yhampe Date: Thu, 25 Jan 2024 10:19:35 +0100 Subject: [PATCH 5/7] RED-6888: Add delete tenant endpoint removed import --- .../controller/external/TenantsController.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java index 07e9f22..470f392 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/controller/external/TenantsController.java @@ -1,7 +1,6 @@ package com.knecon.fforesight.tenantusermanagement.controller.external; import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.CREATE_TENANT; -import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.DELETE_TENANT; import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.DEPLOYMENT_INFO; import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.GET_TENANTS; import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions.UPDATE_TENANT; @@ -9,8 +8,6 @@ import static com.knecon.fforesight.tenantusermanagement.permissions.UserManagem import java.util.List; import java.util.stream.Collectors; -import javax.ws.rs.GET; - import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; @@ -49,6 +46,7 @@ public class TenantsController implements TenantsResource, PublicResource { } } + public void deleteTenant(String tenantId) { try { @@ -83,8 +81,7 @@ public class TenantsController implements TenantsResource, PublicResource { @PreAuthorize("hasAuthority('" + UPDATE_TENANT + "')") - public TenantResponse updateTenant(String tenantId, - @RequestBody TenantRequest tenantRequest) { + public TenantResponse updateTenant(String tenantId, @RequestBody TenantRequest tenantRequest) { TenantResponse tenantResponse = tenantManagementService.updateTenant(tenantId, tenantRequest); return tenantManagementService.removePasswords(tenantResponse); From 0722b505848ac75cee8c8fced8b238fef53820b4 Mon Sep 17 00:00:00 2001 From: yhampe Date: Fri, 26 Jan 2024 08:37:25 +0100 Subject: [PATCH 6/7] RED-6888: Add delete tenant endpoint fixed mistake in endpoint description --- .../tenantusermanagement/api/external/TenantsResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java index 6919be7..f29ec24 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/api/external/TenantsResource.java @@ -38,7 +38,7 @@ public interface TenantsResource { @ResponseBody @ResponseStatus(value = HttpStatus.NO_CONTENT) @Operation(summary = "Deletes given tenant", description = "None") - @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "405", description = "Operation is not allowed."), @ApiResponse(responseCode = "409", description = "Conflict while deleting user.")}) + @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "405", description = "Operation is not allowed."), @ApiResponse(responseCode = "409", description = "Conflict while deleting tenant.")}) @DeleteMapping(value = "/tenants/{tenantId}") void deleteTenant(@PathVariable("tenantId") String tenantId); From d74d4217f86c71435c8cb8af5de0b40dc50fbd6e Mon Sep 17 00:00:00 2001 From: yhampe Date: Fri, 26 Jan 2024 09:29:21 +0100 Subject: [PATCH 7/7] RED-6888: Add delete tenant endpoint review feedback --- .../service/TenantManagementService.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java index 3736347..fb0b56b 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java @@ -197,11 +197,6 @@ public class TenantManagementService implements TenantProvider { TenantResponse tenant = this.getTenant(tenantId); log.info("Requested to delete tenant for: {}", tenant.getTenantId()); - - if (tenant.getS3StorageConnection() != null && tenant.getAzureStorageConnection() != null) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Not possible to set both azure and s3 connection, please only specify one"); - } - log.info("Deleting tenant: {}", tenant.getTenantId()); log.info("Deleting search index for tenant: {}", tenant.getTenantId()); TenantContext.setTenantId(tenant.getTenantId());