diff --git a/build.gradle.kts b/build.gradle.kts
index e9415ce..c229fed 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -11,6 +11,10 @@ plugins {
jacoco
}
+pmd {
+ isConsoleOutput = true
+}
+
configurations {
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
diff --git a/config/pmd/pmd.xml b/config/pmd/pmd.xml
index 503991a..6c558c0 100644
--- a/config/pmd/pmd.xml
+++ b/config/pmd/pmd.xml
@@ -1,15 +1,19 @@
-
+ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
- Knecon main pmd rules
+
+ Knecon ruleset checks the code for bad stuff
+
-
-
+
+
+
+
diff --git a/config/pmd/test_pmd.xml b/config/pmd/test_pmd.xml
index 66fd97c..be4aa92 100644
--- a/config/pmd/test_pmd.xml
+++ b/config/pmd/test_pmd.xml
@@ -1,16 +1,22 @@
-
+ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
+
+
+ Knecon test ruleset checks the code for bad stuff
+
- Knecon test pmd rules
-
-
+
+
+
+
+
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 f29ec24..735505f 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
@@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import com.fasterxml.jackson.databind.JsonNode;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
+import com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest;
import com.knecon.fforesight.tenantusermanagement.model.DeploymentKeyResponse;
import com.knecon.fforesight.tenantusermanagement.model.SimpleTenantResponse;
import com.knecon.fforesight.tenantusermanagement.model.TenantRequest;
@@ -35,10 +36,12 @@ 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 tenant.")})
+ @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);
@@ -61,6 +64,12 @@ public interface TenantsResource {
TenantResponse updateTenant(@PathVariable("tenantId") String tenantId, @RequestBody TenantRequest tenantRequest);
+ @PostMapping(value = "/tenants/{tenantId}/details", consumes = MediaType.APPLICATION_JSON_VALUE)
+ @Operation(summary = "Update details", description = "None")
+ @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
+ void updateDetails(@PathVariable("tenantId") String tenantId, @RequestBody UpdateDetailsRequest request);
+
+
@GetMapping(value = "/tenants/simple", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets all existing tenants in a simplified format", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
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 470f392..ed91a9a 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
@@ -17,6 +17,7 @@ import org.springframework.web.server.ResponseStatusException;
import com.fasterxml.jackson.databind.JsonNode;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
+import com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.api.external.TenantsResource;
import com.knecon.fforesight.tenantusermanagement.model.DeploymentKeyResponse;
@@ -88,6 +89,13 @@ public class TenantsController implements TenantsResource, PublicResource {
}
+ @PreAuthorize("hasAuthority('" + UPDATE_TENANT + "')")
+ public void updateDetails(@PathVariable("tenantId") String tenantId, @RequestBody UpdateDetailsRequest request) {
+
+ tenantManagementService.updateDetails(tenantId, request);
+ }
+
+
@PreAuthorize("hasAuthority('" + DEPLOYMENT_INFO + "')")
public DeploymentKeyResponse getDeploymentKey(@PathVariable(TENANT_ID_PARAM) String tenantId) {
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 fb0b56b..1469eec 100644
--- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java
+++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java
@@ -16,6 +16,7 @@ import java.util.stream.Collectors;
import javax.sql.DataSource;
import javax.ws.rs.NotFoundException;
+import com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest;
import org.apache.commons.lang3.StringUtils;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
@@ -23,6 +24,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.RolesRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
+
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
@@ -96,6 +98,7 @@ public class TenantManagementService implements TenantProvider {
@Value("${fforesight.tenant-exchange.name}")
private String tenantExchangeName;
+
@SneakyThrows
public TenantResponse createTenant(TenantRequest tenantRequest) {
@@ -192,6 +195,7 @@ public class TenantManagementService implements TenantProvider {
}
}
+
@SneakyThrows
public void deleteTenant(String tenantId) {
@@ -205,8 +209,8 @@ public class TenantManagementService implements TenantProvider {
log.info("Dispatched delete index message for tenant: {}", tenant.getTenantId());
deleteSchema(tenant);
- if(tenant.getAzureStorageConnection() != null ) {
- log.info("Deleting azure blob for tenant: {}",tenantId);
+ if (tenant.getAzureStorageConnection() != null) {
+ log.info("Deleting azure blob for tenant: {}", tenantId);
String connectionString = tenant.getAzureStorageConnection().getConnectionString();
String containerName = tenant.getAzureStorageConnection().getContainerName();
@@ -221,26 +225,33 @@ public class TenantManagementService implements TenantProvider {
containerClient.delete();
}
- if(tenant.getS3StorageConnection() != null) {
+ 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());
+ 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);
+ try (var 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);
}
- DeleteBucketRequest deleteBucketRequest = DeleteBucketRequest.builder()
- .bucket(bucketName)
- .expectedBucketOwner(tenantId)
- .build();
- client.deleteBucket(deleteBucketRequest);
}
deleteRealm(tenantId);
tenantRepository.deleteById(tenant.getTenantId());
@@ -323,16 +334,16 @@ public class TenantManagementService implements TenantProvider {
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,
tenant.getDatabaseConnection().getUsername(),
- this.encryptionService.decrypt(tenant.getDatabaseConnection().getPassword())))
- {
+ this.encryptionService.decrypt(tenant.getDatabaseConnection().getPassword()))) {
DataSource tenantDataSource = new SingleConnectionDataSource(connection, false);
JdbcTemplate jdbcTemplate = new JdbcTemplate(tenantDataSource);
- String deleteStatement = "DROP SCHEMA IF EXISTS \"" + tenant.getDatabaseConnection().getSchema() + "\" CASCADE;";
- 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);
@@ -364,14 +375,13 @@ 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) {
+ } catch (Exception e) {
log.warn("Could not delete realm:", e);
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Tenant deletion failed: " + e.getMessage(), e);
}
@@ -483,15 +493,18 @@ public class TenantManagementService implements TenantProvider {
}
}
- private void setPostLogoutRedirectUriForClient(ClientRepresentation client){
- if(client.getAttributes() == null){
+
+ private void setPostLogoutRedirectUriForClient(ClientRepresentation client) {
+
+ if (client.getAttributes() == null) {
client.setAttributes(new HashMap<>());
- }else{
+ } else {
client.setAttributes(new HashMap<>(client.getAttributes()));
}
- client.getAttributes().put("post.logout.redirect.uris","*");
+ client.getAttributes().put("post.logout.redirect.uris", "*");
}
+
private void setPasswordPolicyForRealm(String tenantId) {
var realm = realmService.realm(tenantId).toRepresentation();
@@ -542,7 +555,7 @@ public class TenantManagementService implements TenantProvider {
@Override
@Transactional
- public void updateDetails(String tenantId, com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest request) {
+ public void updateDetails(String tenantId, UpdateDetailsRequest request) {
var tenant = tenantRepository.findById(tenantId);
if (tenant.isEmpty()) {
@@ -551,6 +564,8 @@ public class TenantManagementService implements TenantProvider {
Map details = tenant.get().getDetails() != null ? new HashMap<>(tenant.get().getDetails()) : new HashMap<>();
details.put(request.getKey(), request.getValue());
+ details.entrySet().removeIf(ent -> ent.getValue() == null);
+
tenant.get().setDetails(details);
}
@@ -778,6 +793,7 @@ public class TenantManagementService implements TenantProvider {
}
+
private void updateMasterDisplayName(String displayName) {
log.info("Updating master realm display name: {}", displayName);