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: