Refactor tenant configuration management (#2840)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -88,7 +88,7 @@ class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
|||||||
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
|
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
|
||||||
.andExpect(jsonPath("$._links.configData.href", equalTo(
|
.andExpect(jsonPath("$._links.configData.href", equalTo(
|
||||||
"http://localhost/" + AccessContext.tenant() + "/controller/v1/4712/configData")));
|
"http://localhost/" + AccessContext.tenant() + "/controller/v1/4712/configData")));
|
||||||
Thread.sleep(1); // is required: otherwise processing the next line is
|
waitMillis(1); // is required: otherwise processing the next line is
|
||||||
// often too fast and // the following assert will fail
|
// often too fast and // the following assert will fail
|
||||||
assertThat(targetManagement.getByControllerId("4712")
|
assertThat(targetManagement.getByControllerId("4712")
|
||||||
.getLastTargetQuery())
|
.getLastTargetQuery())
|
||||||
|
|||||||
@@ -33,12 +33,11 @@ import org.springframework.web.context.WebApplicationContext;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Test potential DOS attack scenarios and check if the filter prevents them.
|
* Test potential DOS attack scenarios and check if the filter prevents them.
|
||||||
*/
|
* <p/>
|
||||||
@ActiveProfiles({ "test" })
|
|
||||||
/**
|
|
||||||
* Feature: Component Tests - REST Security<br/>
|
* Feature: Component Tests - REST Security<br/>
|
||||||
* Story: Denial of Service protection filter
|
* Story: Denial of Service protection filter
|
||||||
*/
|
*/
|
||||||
|
@ActiveProfiles({ "test" })
|
||||||
class DosFilterTest extends AbstractDDiApiIntegrationTest {
|
class DosFilterTest extends AbstractDDiApiIntegrationTest {
|
||||||
|
|
||||||
private static final String X_FORWARDED_FOR = HawkbitSecurityProperties.Clients.X_FORWARDED_FOR;
|
private static final String X_FORWARDED_FOR = HawkbitSecurityProperties.Clients.X_FORWARDED_FOR;
|
||||||
@@ -113,7 +112,7 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
|
|||||||
void acceptableGetLoad() throws Exception {
|
void acceptableGetLoad() throws Exception {
|
||||||
for (int x = 0; x < 3; x++) {
|
for (int x = 0; x < 3; x++) {
|
||||||
// sleep for one second
|
// sleep for one second
|
||||||
Thread.sleep(1100);
|
waitMillis(1100);
|
||||||
for (int i = 0; i < 9; i++) {
|
for (int i = 0; i < 9; i++) {
|
||||||
mvc.perform(get("/{tenant}/controller/v1/4711", AccessContext.tenant())
|
mvc.perform(get("/{tenant}/controller/v1/4711", AccessContext.tenant())
|
||||||
.header(X_FORWARDED_FOR, "10.0.0.1"))
|
.header(X_FORWARDED_FOR, "10.0.0.1"))
|
||||||
@@ -159,7 +158,7 @@ class DosFilterTest extends AbstractDDiApiIntegrationTest {
|
|||||||
|
|
||||||
for (int x = 0; x < 5; x++) {
|
for (int x = 0; x < 5; x++) {
|
||||||
// sleep for one second
|
// sleep for one second
|
||||||
Thread.sleep(1100);
|
waitMillis(1100);
|
||||||
|
|
||||||
for (int i = 0; i < 9; i++) {
|
for (int i = 0; i < 9; i++) {
|
||||||
mvc.perform(post("/{tenant}/controller/v1/4711/deploymentBase/" + actionId + "/feedback",
|
mvc.perform(post("/{tenant}/controller/v1/4711/deploymentBase/" + actionId + "/feedback",
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ package org.eclipse.hawkbit.mgmt.rest.api;
|
|||||||
|
|
||||||
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.TENANT_ORDER;
|
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.TENANT_ORDER;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -28,6 +26,7 @@ import org.eclipse.hawkbit.mgmt.json.model.system.MgmtSystemTenantConfigurationV
|
|||||||
import org.eclipse.hawkbit.rest.OpenApi;
|
import org.eclipse.hawkbit.rest.OpenApi;
|
||||||
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
||||||
import org.springframework.hateoas.MediaTypes;
|
import org.springframework.hateoas.MediaTypes;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
@@ -35,6 +34,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* REST Resource for handling tenant specific configuration operations.
|
* REST Resource for handling tenant specific configuration operations.
|
||||||
@@ -74,38 +74,6 @@ public interface MgmtTenantManagementRestApi {
|
|||||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<Map<String, MgmtSystemTenantConfigurationValue>> getTenantConfiguration();
|
ResponseEntity<Map<String, MgmtSystemTenantConfigurationValue>> getTenantConfiguration();
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the DELETE request of deleting a tenant specific configuration value.
|
|
||||||
*
|
|
||||||
* @param keyName the Name of the configuration key
|
|
||||||
* @return if the given configuration value exists and could be deleted HTTP OK. In any failure the JsonResponseExceptionHandler is handling
|
|
||||||
* the response.
|
|
||||||
*/
|
|
||||||
@Operation(summary = "Delete a tenant specific configuration value", description = "The DELETE request removes a " +
|
|
||||||
"tenant specific configuration value for the tenant. Afterwards the global default value is used. " +
|
|
||||||
"Required Permission: TENANT_CONFIGURATION")
|
|
||||||
@ApiResponses(value = {
|
|
||||||
@ApiResponse(responseCode = "204", description = "Successfully deleted"),
|
|
||||||
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
|
|
||||||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
|
|
||||||
@ApiResponse(responseCode = "401", description = "The request requires user auth.",
|
|
||||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
|
||||||
@ApiResponse(responseCode = "403",
|
|
||||||
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
|
|
||||||
"data volume restriction applies.",
|
|
||||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
|
||||||
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
|
|
||||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
|
||||||
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
|
|
||||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
|
||||||
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
|
|
||||||
"and the client has to wait another second.",
|
|
||||||
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
|
||||||
})
|
|
||||||
@DeleteMapping(value = MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
|
||||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
|
||||||
ResponseEntity<Void> deleteTenantConfigurationValue(@PathVariable("keyName") String keyName);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the GET request of receiving a tenant specific configuration value.
|
* Handles the GET request of receiving a tenant specific configuration value.
|
||||||
*
|
*
|
||||||
@@ -138,16 +106,13 @@ public interface MgmtTenantManagementRestApi {
|
|||||||
})
|
})
|
||||||
@GetMapping(value = MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
@GetMapping(value = MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
||||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<MgmtSystemTenantConfigurationValue> getTenantConfigurationValue(
|
ResponseEntity<MgmtSystemTenantConfigurationValue> getTenantConfigurationValue(@PathVariable("keyName") String keyName);
|
||||||
@PathVariable("keyName") String keyName);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the PUT request for updating a tenant specific configuration value.
|
* Handles the PUT request for updating a tenant specific configuration value.
|
||||||
*
|
*
|
||||||
* @param keyName the name of the configuration key
|
* @param keyName the name of the configuration key
|
||||||
* @param configurationValueRest the new value for the configuration
|
* @param configurationValueRest the new value for the configuration
|
||||||
* @return if the given configuration value exists and could be get HTTP OK. In any failure the JsonResponseExceptionHandler is handling the
|
|
||||||
* response.
|
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "Update a tenant specific configuration value.", description = "The PUT request changes a " +
|
@Operation(summary = "Update a tenant specific configuration value.", description = "The PUT request changes a " +
|
||||||
"configuration value of a specific configuration key for the tenant. " +
|
"configuration value of a specific configuration key for the tenant. " +
|
||||||
@@ -181,7 +146,8 @@ public interface MgmtTenantManagementRestApi {
|
|||||||
@PutMapping(value = MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
@PutMapping(value = MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
||||||
consumes = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE },
|
consumes = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE },
|
||||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<MgmtSystemTenantConfigurationValue> updateTenantConfigurationValue(
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
void updateTenantConfigurationValue(
|
||||||
@PathVariable("keyName") String keyName,
|
@PathVariable("keyName") String keyName,
|
||||||
@RequestBody MgmtSystemTenantConfigurationValueRequest configurationValueRest);
|
@RequestBody MgmtSystemTenantConfigurationValueRequest configurationValueRest);
|
||||||
|
|
||||||
@@ -189,8 +155,6 @@ public interface MgmtTenantManagementRestApi {
|
|||||||
* Handles the PUT request for updating a batch of tenant specific configurations
|
* Handles the PUT request for updating a batch of tenant specific configurations
|
||||||
*
|
*
|
||||||
* @param configurationValueMap a Map of name - value pairs for the configurations
|
* @param configurationValueMap a Map of name - value pairs for the configurations
|
||||||
* @return if the given configurations values exists and could be get HTTP OK. In any failure the JsonResponseExceptionHandler is handling
|
|
||||||
* the response.
|
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "Batch update of tenant configuration.", description = "The PUT request updates the whole " +
|
@Operation(summary = "Batch update of tenant configuration.", description = "The PUT request updates the whole " +
|
||||||
"configuration for the tenant. Required Permission: TENANT_CONFIGURATION")
|
"configuration for the tenant. Required Permission: TENANT_CONFIGURATION")
|
||||||
@@ -221,6 +185,39 @@ public interface MgmtTenantManagementRestApi {
|
|||||||
@PutMapping(value = MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs",
|
@PutMapping(value = MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs",
|
||||||
consumes = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE },
|
consumes = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE },
|
||||||
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<List<MgmtSystemTenantConfigurationValue>> updateTenantConfiguration(
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
@RequestBody Map<String, Serializable> configurationValueMap);
|
void updateTenantConfiguration(@RequestBody Map<String, Object> configurationValueMap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the DELETE request of deleting a tenant specific configuration value.
|
||||||
|
*
|
||||||
|
* @param keyName the Name of the configuration key
|
||||||
|
*/
|
||||||
|
@Operation(summary = "Delete a tenant specific configuration value", description = "The DELETE request removes a " +
|
||||||
|
"tenant specific configuration value for the tenant. Afterwards the global default value is used. " +
|
||||||
|
"Required Permission: TENANT_CONFIGURATION")
|
||||||
|
@ApiResponses(value = {
|
||||||
|
@ApiResponse(responseCode = "204", description = "Successfully deleted"),
|
||||||
|
@ApiResponse(responseCode = "400", description = "Bad Request - e.g. invalid parameters",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ExceptionInfo.class))),
|
||||||
|
@ApiResponse(responseCode = "401", description = "The request requires user auth.",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||||
|
@ApiResponse(responseCode = "403",
|
||||||
|
description = "Insufficient permissions, entity is not allowed to be changed (i.e. read-only) or " +
|
||||||
|
"data volume restriction applies.",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||||
|
@ApiResponse(responseCode = "404", description = "The key to remove is not found.",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||||
|
@ApiResponse(responseCode = "405", description = "The http request method is not allowed on the resource.",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||||
|
@ApiResponse(responseCode = "406", description = "In case accept header is specified and not application/json.",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true))),
|
||||||
|
@ApiResponse(responseCode = "429", description = "Too many requests. The server will refuse further attempts " +
|
||||||
|
"and the client has to wait another second.",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(hidden = true)))
|
||||||
|
})
|
||||||
|
@DeleteMapping(value = MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
||||||
|
produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
|
||||||
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
void deleteTenantConfigurationValue(@PathVariable("keyName") String keyName);
|
||||||
}
|
}
|
||||||
@@ -9,9 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -22,10 +20,10 @@ import org.eclipse.hawkbit.mgmt.json.model.system.MgmtSystemTenantConfigurationV
|
|||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTenantManagementRestApi;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTenantManagementRestApi;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.resource.mapper.MgmtTenantManagementMapper;
|
import org.eclipse.hawkbit.mgmt.rest.resource.mapper.MgmtTenantManagementMapper;
|
||||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||||
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
|
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
|
||||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||||
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
|
|
||||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@@ -70,20 +68,6 @@ public class MgmtTenantManagementResource implements MgmtTenantManagementRestApi
|
|||||||
return ResponseEntity.ok(tenantConfigurationValueMap);
|
return ResponseEntity.ok(tenantConfigurationValueMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@AuditLog(entity = "TenantConfiguration", type = AuditLog.Type.DELETE, description = "Delete AccessContext Configuration Value")
|
|
||||||
public ResponseEntity<Void> deleteTenantConfigurationValue(final String keyName) {
|
|
||||||
// Default DistributionSet Type cannot be deleted as is part of TenantMetadata
|
|
||||||
if (isDefaultDistributionSetTypeKey(keyName)) {
|
|
||||||
return ResponseEntity.badRequest().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
TenantConfigHelper.getTenantConfigurationManagement().deleteConfiguration(keyName);
|
|
||||||
|
|
||||||
log.debug("{} config value deleted, return status {}", keyName, HttpStatus.OK);
|
|
||||||
return ResponseEntity.noContent().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<MgmtSystemTenantConfigurationValue> getTenantConfigurationValue(final String keyName) {
|
public ResponseEntity<MgmtSystemTenantConfigurationValue> getTenantConfigurationValue(final String keyName) {
|
||||||
return ResponseEntity.ok(loadTenantConfigurationValueBy(keyName));
|
return ResponseEntity.ok(loadTenantConfigurationValueBy(keyName));
|
||||||
@@ -91,61 +75,56 @@ public class MgmtTenantManagementResource implements MgmtTenantManagementRestApi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@AuditLog(entity = "TenantConfiguration", type = AuditLog.Type.UPDATE, description = "Update AccessContext Configuration Value")
|
@AuditLog(entity = "TenantConfiguration", type = AuditLog.Type.UPDATE, description = "Update AccessContext Configuration Value")
|
||||||
public ResponseEntity<MgmtSystemTenantConfigurationValue> updateTenantConfigurationValue(
|
public void updateTenantConfigurationValue(final String keyName, final MgmtSystemTenantConfigurationValueRequest configurationValueRest) {
|
||||||
final String keyName, final MgmtSystemTenantConfigurationValueRequest configurationValueRest) {
|
final Object configurationValue = configurationValueRest.getValue();
|
||||||
Serializable configurationValue = configurationValueRest.getValue();
|
|
||||||
final MgmtSystemTenantConfigurationValue responseUpdatedValue;
|
|
||||||
if (isDefaultDistributionSetTypeKey(keyName)) {
|
if (isDefaultDistributionSetTypeKey(keyName)) {
|
||||||
responseUpdatedValue = updateDefaultDsType(configurationValue);
|
updateDefaultDsType(configurationValue);
|
||||||
} else {
|
} else {
|
||||||
final TenantConfigurationValue<? extends Serializable> updatedTenantConfigurationValue = TenantConfigHelper
|
TenantConfigHelper
|
||||||
.getTenantConfigurationManagement()
|
.getTenantConfigurationManagement()
|
||||||
.addOrUpdateConfiguration(keyName, configurationValueRest.getValue());
|
.addOrUpdateConfiguration(keyName, configurationValueRest.getValue());
|
||||||
responseUpdatedValue = MgmtTenantManagementMapper.toResponseTenantConfigurationValue(keyName, updatedTenantConfigurationValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(responseUpdatedValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@AuditLog(entity = "TenantConfiguration", type = AuditLog.Type.UPDATE, description = "Update AccessContext Configuration")
|
@AuditLog(entity = "TenantConfiguration", type = AuditLog.Type.UPDATE, description = "Update AccessContext Configuration")
|
||||||
public ResponseEntity<List<MgmtSystemTenantConfigurationValue>> updateTenantConfiguration(
|
public void updateTenantConfiguration(final Map<String, Object> configurationValueMap) {
|
||||||
final Map<String, Serializable> configurationValueMap) {
|
|
||||||
final boolean containsNull = configurationValueMap.keySet().stream().anyMatch(Objects::isNull);
|
final boolean containsNull = configurationValueMap.keySet().stream().anyMatch(Objects::isNull);
|
||||||
|
|
||||||
if (containsNull) {
|
if (containsNull) {
|
||||||
return ResponseEntity.badRequest().build();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Try update TenantMetadata first
|
// try update TenantMetadata first
|
||||||
final Serializable defaultDsTypeValueUpdate = configurationValueMap.remove(MgmtTenantManagementMapper.DEFAULT_DISTRIBUTION_SET_TYPE_KEY);
|
final Object defaultDsTypeValueUpdate = configurationValueMap.remove(MgmtTenantManagementMapper.DEFAULT_DISTRIBUTION_SET_TYPE_KEY);
|
||||||
Long oldDefaultDsType = null;
|
Long oldDefaultDsType = null;
|
||||||
MgmtSystemTenantConfigurationValue updatedDefaultDsType = null;
|
MgmtSystemTenantConfigurationValue updatedDefaultDsType = null;
|
||||||
if (defaultDsTypeValueUpdate != null) {
|
if (defaultDsTypeValueUpdate != null) {
|
||||||
oldDefaultDsType = systemManagement.getTenantMetadata().getDefaultDsType().getId();
|
oldDefaultDsType = systemManagement.getTenantMetadata().getDefaultDsType().getId();
|
||||||
updatedDefaultDsType = updateDefaultDsType(defaultDsTypeValueUpdate);
|
updatedDefaultDsType = updateDefaultDsType(defaultDsTypeValueUpdate);
|
||||||
}
|
}
|
||||||
//try update TenantConfiguration, in case of Error -> rollback TenantMetadata
|
// try update TenantConfiguration, in case of Error -> rollback TenantMetadata
|
||||||
final Map<String, TenantConfigurationValue<Serializable>> tenantConfigurationValues;
|
|
||||||
try {
|
try {
|
||||||
tenantConfigurationValues = TenantConfigHelper.getTenantConfigurationManagement().addOrUpdateConfiguration(configurationValueMap);
|
TenantConfigHelper.getTenantConfigurationManagement().addOrUpdateConfiguration(configurationValueMap);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
//if DefaultDsType was updated, rollback it in case of TenantConfiguration update.
|
// if DefaultDsType was updated, rollback it in case of TenantConfiguration update.
|
||||||
if (updatedDefaultDsType != null) {
|
if (updatedDefaultDsType != null) {
|
||||||
systemManagement.updateTenantMetadata(oldDefaultDsType);
|
systemManagement.updateTenantMetadata(oldDefaultDsType);
|
||||||
}
|
}
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<MgmtSystemTenantConfigurationValue> tenantConfigurationListUpdated = new java.util.ArrayList<>(
|
|
||||||
tenantConfigurationValues.entrySet().stream()
|
|
||||||
.map(entry -> MgmtTenantManagementMapper.toResponseTenantConfigurationValue(entry.getKey(), entry.getValue()))
|
|
||||||
.toList());
|
|
||||||
if (updatedDefaultDsType != null) {
|
|
||||||
tenantConfigurationListUpdated.add(updatedDefaultDsType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(tenantConfigurationListUpdated);
|
@Override
|
||||||
|
@AuditLog(entity = "TenantConfiguration", type = AuditLog.Type.DELETE, description = "Delete AccessContext Configuration Value")
|
||||||
|
public void deleteTenantConfigurationValue(final String keyName) {
|
||||||
|
// Default DistributionSet Type cannot be deleted as is part of TenantMetadata
|
||||||
|
if (isDefaultDistributionSetTypeKey(keyName)) {
|
||||||
|
throw new EntityNotFoundException("Configuration key not found", keyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TenantConfigHelper.getTenantConfigurationManagement().deleteConfiguration(keyName);
|
||||||
|
log.debug("{} config value deleted", keyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isDefaultDistributionSetTypeKey(String keyName) {
|
private static boolean isDefaultDistributionSetTypeKey(String keyName) {
|
||||||
@@ -164,7 +143,7 @@ public class MgmtTenantManagementResource implements MgmtTenantManagementRestApi
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MgmtSystemTenantConfigurationValue updateDefaultDsType(Serializable defaultDsType) {
|
private MgmtSystemTenantConfigurationValue updateDefaultDsType(final Object defaultDsType) {
|
||||||
final long updateDefaultDsType;
|
final long updateDefaultDsType;
|
||||||
try {
|
try {
|
||||||
updateDefaultDsType = ((Number) defaultDsType).longValue();
|
updateDefaultDsType = ((Number) defaultDsType).longValue();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||||
|
|
||||||
|
import static org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING;
|
||||||
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.callAs;
|
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.callAs;
|
||||||
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.getAs;
|
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.getAs;
|
||||||
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.withUser;
|
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.withUser;
|
||||||
@@ -28,7 +29,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.eclipse.hawkbit.auth.SpPermission;
|
import org.eclipse.hawkbit.auth.SpPermission;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.system.MgmtSystemTenantConfigurationValueRequest;
|
import org.eclipse.hawkbit.mgmt.json.model.system.MgmtSystemTenantConfigurationValueRequest;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||||
@@ -52,13 +52,12 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void getTenantConfigurations() throws Exception {
|
void getTenantConfigurations() throws Exception {
|
||||||
mvc.perform(get(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs"))
|
mvc.perform(get(SYSTEM_V1_REQUEST_MAPPING + "/configs"))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
//check for TenantMetadata additional properties
|
// check for TenantMetadata additional properties
|
||||||
.andExpect(jsonPath("$.['" + DEFAULT_DISTRIBUTION_SET_TYPE_KEY + "']").exists())
|
.andExpect(jsonPath("$.['" + DEFAULT_DISTRIBUTION_SET_TYPE_KEY + "']").exists())
|
||||||
.andExpect(jsonPath("$.['" + DEFAULT_DISTRIBUTION_SET_TYPE_KEY + "'].value", equalTo(getActualDefaultDsType().intValue())));
|
.andExpect(jsonPath("$.['" + DEFAULT_DISTRIBUTION_SET_TYPE_KEY + "'].value", equalTo(getActualDefaultDsType().intValue())));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,8 +65,8 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void getTenantConfiguration() throws Exception {
|
void getTenantConfiguration() throws Exception {
|
||||||
//Test TenantConfiguration property
|
// test TenantConfiguration property
|
||||||
mvc.perform(get(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY))
|
mvc.perform(get(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
}
|
}
|
||||||
@@ -77,9 +76,8 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void getTenantMetadata() throws Exception {
|
void getTenantMetadata() throws Exception {
|
||||||
//Test TenantMetadata property
|
// test TenantMetadata property
|
||||||
mvc.perform(get(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
mvc.perform(get(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", DEFAULT_DISTRIBUTION_SET_TYPE_KEY))
|
||||||
DEFAULT_DISTRIBUTION_SET_TYPE_KEY))
|
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.value", equalTo(getActualDefaultDsType().intValue())));
|
.andExpect(jsonPath("$.value", equalTo(getActualDefaultDsType().intValue())));
|
||||||
@@ -95,11 +93,11 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
final ObjectMapper mapper = new ObjectMapper();
|
final ObjectMapper mapper = new ObjectMapper();
|
||||||
final String json = mapper.writeValueAsString(bodyPut);
|
final String json = mapper.writeValueAsString(bodyPut);
|
||||||
|
|
||||||
mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
mvc.perform(put(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY)
|
||||||
AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY).content(json)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.content(json))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isNoContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,17 +107,17 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
void putTenantMetadata() throws Exception {
|
void putTenantMetadata() throws Exception {
|
||||||
final MgmtSystemTenantConfigurationValueRequest bodyPut = new MgmtSystemTenantConfigurationValueRequest();
|
final MgmtSystemTenantConfigurationValueRequest bodyPut = new MgmtSystemTenantConfigurationValueRequest();
|
||||||
|
|
||||||
long updatedTestDefaultDsType = createTestDistributionSetType();
|
final long updatedTestDefaultDsType = createTestDistributionSetType();
|
||||||
bodyPut.setValue(updatedTestDefaultDsType);
|
bodyPut.setValue(updatedTestDefaultDsType);
|
||||||
|
|
||||||
final ObjectMapper mapper = new ObjectMapper();
|
final ObjectMapper mapper = new ObjectMapper();
|
||||||
final String json = mapper.writeValueAsString(bodyPut);
|
final String json = mapper.writeValueAsString(bodyPut);
|
||||||
|
|
||||||
mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
mvc.perform(put(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", DEFAULT_DISTRIBUTION_SET_TYPE_KEY)
|
||||||
DEFAULT_DISTRIBUTION_SET_TYPE_KEY).content(json)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.content(json))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
//check if after Rest success, value is really changed in TenantMetadata
|
//check if after Rest success, value is really changed in TenantMetadata
|
||||||
assertEquals(updatedTestDefaultDsType, getActualDefaultDsType(),
|
assertEquals(updatedTestDefaultDsType, getActualDefaultDsType(),
|
||||||
@@ -131,14 +129,14 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void putTenantMetadataFails() throws Exception {
|
void putTenantMetadataFails() throws Exception {
|
||||||
long oldDefaultDsType = getActualDefaultDsType();
|
final long oldDefaultDsType = getActualDefaultDsType();
|
||||||
//try an invalid input
|
// try an invalid input
|
||||||
String newDefaultDsType = new JSONObject().put("value", true).toString();
|
String newDefaultDsType = new JSONObject().put("value", true).toString();
|
||||||
assertDefaultDsTypeUpdateBadRequestFails(newDefaultDsType, oldDefaultDsType, status().isBadRequest());
|
assertDefaultDsTypeUpdateBadRequestFails(newDefaultDsType, oldDefaultDsType, status().isBadRequest());
|
||||||
//try an invalid input
|
// try an invalid input
|
||||||
newDefaultDsType = new JSONObject().put("value", "someInvalidInput").toString();
|
newDefaultDsType = new JSONObject().put("value", "someInvalidInput").toString();
|
||||||
assertDefaultDsTypeUpdateBadRequestFails(newDefaultDsType, oldDefaultDsType, status().isBadRequest());
|
assertDefaultDsTypeUpdateBadRequestFails(newDefaultDsType, oldDefaultDsType, status().isBadRequest());
|
||||||
//try valid input, but the given DistributionSetType Id does not exist..
|
// try valid input, but the given DistributionSetType Id does not exist..
|
||||||
newDefaultDsType = new JSONObject().put("value", 99999).toString();
|
newDefaultDsType = new JSONObject().put("value", 99999).toString();
|
||||||
assertDefaultDsTypeUpdateBadRequestFails(newDefaultDsType, oldDefaultDsType, status().isNotFound());
|
assertDefaultDsTypeUpdateBadRequestFails(newDefaultDsType, oldDefaultDsType, status().isNotFound());
|
||||||
}
|
}
|
||||||
@@ -151,13 +149,15 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
final String bodyActivate = new JSONObject().put("value", true).toString();
|
final String bodyActivate = new JSONObject().put("value", true).toString();
|
||||||
final String bodyDeactivate = new JSONObject().put("value", false).toString();
|
final String bodyDeactivate = new JSONObject().put("value", false).toString();
|
||||||
|
|
||||||
mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", MULTI_ASSIGNMENTS_ENABLED)
|
mvc.perform(put(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", MULTI_ASSIGNMENTS_ENABLED)
|
||||||
.content(bodyActivate).contentType(MediaType.APPLICATION_JSON))
|
.content(bodyActivate)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", MULTI_ASSIGNMENTS_ENABLED)
|
mvc.perform(put(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", MULTI_ASSIGNMENTS_ENABLED)
|
||||||
.content(bodyDeactivate).contentType(MediaType.APPLICATION_JSON))
|
.content(bodyDeactivate)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isForbidden());
|
||||||
}
|
}
|
||||||
@@ -167,14 +167,15 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void changeBatchConfigurationShouldFailOnInvalidTenantConfiguration() throws Exception {
|
void changeBatchConfigurationShouldFailOnInvalidTenantConfiguration() throws Exception {
|
||||||
//in this scenario
|
// in this scenario
|
||||||
// some TenantConfiguration are not valid,
|
// some TenantConfiguration are not valid,
|
||||||
// TenantMetadata - DefaultDSType ID is valid,
|
// TenantMetadata - DefaultDSType ID is valid,
|
||||||
//in the end batch configuration update must fail, and thus, not a single config should be actually changed
|
// in the end batch configuration update must fail, and thus, not a single config should be actually changed
|
||||||
long testValidDistributionSetType = createTestDistributionSetType();
|
long testValidDistributionSetType = createTestDistributionSetType();
|
||||||
boolean oldRolloutApprovalConfig = (Boolean) tenantConfigurationManagement().getConfigurationValue(ROLLOUT_APPROVAL_ENABLED).getValue();
|
boolean oldRolloutApprovalConfig = (Boolean) tenantConfigurationManagement().getConfigurationValue(ROLLOUT_APPROVAL_ENABLED).getValue();
|
||||||
String oldAuthGatewayToken = (String) tenantConfigurationManagement().getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY).getValue();
|
String oldAuthGatewayToken = (String) tenantConfigurationManagement().getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY)
|
||||||
//test TenantConfiguration with invalid config value, and a valid TenantMetadata - Default DistributionSetType id
|
.getValue();
|
||||||
|
// test TenantConfiguration with invalid config value, and a valid TenantMetadata - Default DistributionSetType id
|
||||||
assertBatchConfigurationFails(!oldRolloutApprovalConfig, "invalid-config-value", oldAuthGatewayToken + "randomSuffix0",
|
assertBatchConfigurationFails(!oldRolloutApprovalConfig, "invalid-config-value", oldAuthGatewayToken + "randomSuffix0",
|
||||||
testValidDistributionSetType, status().isBadRequest());
|
testValidDistributionSetType, status().isBadRequest());
|
||||||
}
|
}
|
||||||
@@ -184,29 +185,31 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void changeBatchConfigurationShouldOnInvalidTenantMetadata() throws Exception {
|
void changeBatchConfigurationShouldOnInvalidTenantMetadata() throws Exception {
|
||||||
//in this scenario
|
// in this scenario
|
||||||
// all TenantConfiguration have valid and new values - using old values, inverted
|
// all TenantConfiguration have valid and new values - using old values, inverted
|
||||||
// TenantMetadata - DefaultDSType ID is invalid
|
// TenantMetadata - DefaultDSType ID is invalid
|
||||||
//in the end batch configuration update must fail, and thus, not a single config should be actually changed.
|
// in the end batch configuration update must fail, and thus, not a single config should be actually changed.
|
||||||
boolean oldRolloutApprovalConfig = (Boolean) tenantConfigurationManagement().getConfigurationValue(ROLLOUT_APPROVAL_ENABLED).getValue();
|
final boolean oldRolloutApprovalConfig = (Boolean) tenantConfigurationManagement()
|
||||||
boolean oldAuthGatewayTokenEnabled = (Boolean) tenantConfigurationManagement()
|
.getConfigurationValue(ROLLOUT_APPROVAL_ENABLED).getValue();
|
||||||
|
final boolean oldAuthGatewayTokenEnabled = (Boolean) tenantConfigurationManagement()
|
||||||
.getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED).getValue();
|
.getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED).getValue();
|
||||||
String oldAuthGatewayToken = (String) tenantConfigurationManagement().getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY).getValue();
|
final String oldAuthGatewayToken = (String) tenantConfigurationManagement()
|
||||||
|
.getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY).getValue();
|
||||||
|
|
||||||
//invalid TenantMetadata Default DistributionSetType, it is expected to be a number. Testing invalid type - string
|
// invalid TenantMetadata Default DistributionSetType, it is expected to be a number. Testing invalid type - string
|
||||||
//not a single configuration should be changed after the failure
|
// not a single configuration should be changed after the failure
|
||||||
Object testInvalidDistributionSetType = "someInvalidInput";
|
Object testInvalidDistributionSetType = "someInvalidInput";
|
||||||
assertBatchConfigurationFails(!oldRolloutApprovalConfig, !oldAuthGatewayTokenEnabled, oldAuthGatewayToken + "randomSuffix1",
|
assertBatchConfigurationFails(!oldRolloutApprovalConfig, !oldAuthGatewayTokenEnabled, oldAuthGatewayToken + "randomSuffix1",
|
||||||
testInvalidDistributionSetType, status().isBadRequest());
|
testInvalidDistributionSetType, status().isBadRequest());
|
||||||
|
|
||||||
//invalid TenantMetadata Default DistributionSetType, it is expected to be a number. Testing invalid type - bool
|
// invalid TenantMetadata Default DistributionSetType, it is expected to be a number. Testing invalid type - bool
|
||||||
//not a single configuration should be changed after the failure
|
// not a single configuration should be changed after the failure
|
||||||
testInvalidDistributionSetType = true;
|
testInvalidDistributionSetType = true;
|
||||||
assertBatchConfigurationFails(!oldRolloutApprovalConfig, !oldAuthGatewayTokenEnabled, oldAuthGatewayToken + "randomSuffix2",
|
assertBatchConfigurationFails(!oldRolloutApprovalConfig, !oldAuthGatewayTokenEnabled, oldAuthGatewayToken + "randomSuffix2",
|
||||||
testInvalidDistributionSetType, status().isBadRequest());
|
testInvalidDistributionSetType, status().isBadRequest());
|
||||||
|
|
||||||
//Valid TenantMetadata Default DistributionSetType, it is expected to be a number. Testing valid type - but given DistributionSetType Id does not exist.
|
// valid TenantMetadata Default DistributionSetType, it is expected to be a number. Testing valid type - but given DistributionSetType Id does not exist.
|
||||||
//not a single configuration should be changed after the failure
|
// not a single configuration should be changed after the failure
|
||||||
testInvalidDistributionSetType = 9999;
|
testInvalidDistributionSetType = 9999;
|
||||||
assertBatchConfigurationFails(!oldRolloutApprovalConfig, !oldAuthGatewayTokenEnabled, oldAuthGatewayToken + "randomSuffix2",
|
assertBatchConfigurationFails(!oldRolloutApprovalConfig, !oldAuthGatewayTokenEnabled, oldAuthGatewayToken + "randomSuffix2",
|
||||||
testInvalidDistributionSetType, status().isNotFound());
|
testInvalidDistributionSetType, status().isNotFound());
|
||||||
@@ -217,23 +220,23 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void changeBatchConfiguration() throws Exception {
|
void changeBatchConfiguration() throws Exception {
|
||||||
long updatedDistributionSetType = createTestDistributionSetType();
|
final long updatedDistributionSetType = createTestDistributionSetType();
|
||||||
boolean updatedRolloutApprovalEnabled = true;
|
final boolean updatedRolloutApprovalEnabled = true;
|
||||||
boolean updatedAuthGatewayTokenEnabled = true;
|
final boolean updatedAuthGatewayTokenEnabled = true;
|
||||||
String updatedAuthGatewayTokenKey = "54321";
|
final String updatedAuthGatewayTokenKey = "54321";
|
||||||
JSONObject configuration = new JSONObject();
|
final JSONObject configuration = new JSONObject();
|
||||||
configuration.put(ROLLOUT_APPROVAL_ENABLED, updatedRolloutApprovalEnabled);
|
configuration.put(ROLLOUT_APPROVAL_ENABLED, updatedRolloutApprovalEnabled);
|
||||||
configuration.put(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED, updatedAuthGatewayTokenEnabled);
|
configuration.put(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED, updatedAuthGatewayTokenEnabled);
|
||||||
configuration.put(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, updatedAuthGatewayTokenKey);
|
configuration.put(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, updatedAuthGatewayTokenKey);
|
||||||
configuration.put(DEFAULT_DISTRIBUTION_SET_TYPE_KEY, updatedDistributionSetType);
|
configuration.put(DEFAULT_DISTRIBUTION_SET_TYPE_KEY, updatedDistributionSetType);
|
||||||
|
|
||||||
String body = configuration.toString();
|
final String body = configuration.toString();
|
||||||
|
|
||||||
mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs") .content(body).contentType(MediaType.APPLICATION_JSON))
|
mvc.perform(put(SYSTEM_V1_REQUEST_MAPPING + "/configs").content(body).contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
//assert all changes were applied after Rest Success
|
// assert all changes were applied after Rest Success
|
||||||
assertEquals(updatedDistributionSetType, getActualDefaultDsType(),
|
assertEquals(updatedDistributionSetType, getActualDefaultDsType(),
|
||||||
"Change BatchConfiguration was successful but TenantMetadata - Default DistributionSetType was not actually changed.");
|
"Change BatchConfiguration was successful but TenantMetadata - Default DistributionSetType was not actually changed.");
|
||||||
assertEquals(updatedRolloutApprovalEnabled, tenantConfigurationManagement().getConfigurationValue(ROLLOUT_APPROVAL_ENABLED).getValue(),
|
assertEquals(updatedRolloutApprovalEnabled, tenantConfigurationManagement().getConfigurationValue(ROLLOUT_APPROVAL_ENABLED).getValue(),
|
||||||
@@ -255,20 +258,23 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
final String bodyDeactivate = new JSONObject().put("value", false).toString();
|
final String bodyDeactivate = new JSONObject().put("value", false).toString();
|
||||||
|
|
||||||
// enable Multi-Assignments
|
// enable Multi-Assignments
|
||||||
mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", MULTI_ASSIGNMENTS_ENABLED)
|
mvc.perform(put(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", MULTI_ASSIGNMENTS_ENABLED)
|
||||||
.content(bodyActivate).contentType(MediaType.APPLICATION_JSON))
|
.content(bodyActivate)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
// try to enable Auto-Close
|
// try to enable Auto-Close
|
||||||
mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED)
|
mvc.perform(put(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED)
|
||||||
.content(bodyActivate).contentType(MediaType.APPLICATION_JSON))
|
.content(bodyActivate)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isForbidden());
|
||||||
|
|
||||||
// try to disable Auto-Close
|
// try to disable Auto-Close
|
||||||
mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED)
|
mvc.perform(put(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED)
|
||||||
.content(bodyDeactivate).contentType(MediaType.APPLICATION_JSON))
|
.content(bodyDeactivate)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isForbidden());
|
||||||
}
|
}
|
||||||
@@ -278,8 +284,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void deleteTenantConfiguration() throws Exception {
|
void deleteTenantConfiguration() throws Exception {
|
||||||
mvc.perform(delete(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
mvc.perform(delete(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY))
|
||||||
AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY))
|
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isNoContent());
|
.andExpect(status().isNoContent());
|
||||||
}
|
}
|
||||||
@@ -289,10 +294,9 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void deleteTenantMetadataFail() throws Exception {
|
void deleteTenantMetadataFail() throws Exception {
|
||||||
mvc.perform(delete(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
mvc.perform(delete(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", DEFAULT_DISTRIBUTION_SET_TYPE_KEY))
|
||||||
DEFAULT_DISTRIBUTION_SET_TYPE_KEY))
|
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isBadRequest());
|
.andExpect(status().isNotFound());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -301,15 +305,14 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
@Test
|
@Test
|
||||||
void getTenantConfigurationReadGWToken() throws Exception {
|
void getTenantConfigurationReadGWToken() throws Exception {
|
||||||
getAs(withUser("tenant_admin", SpPermission.TENANT_CONFIGURATION), () -> {
|
getAs(withUser("tenant_admin", SpPermission.TENANT_CONFIGURATION), () -> {
|
||||||
tenantConfigurationManagement().addOrUpdateConfiguration(
|
tenantConfigurationManagement().addOrUpdateConfiguration(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "123");
|
||||||
AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "123");
|
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO - should be able to read with TENANT_CONFIGURATION but somehow here the role hierarchy doesn't play
|
// TODO - should be able to read with TENANT_CONFIGURATION but somehow here the role hierarchy doesn't play
|
||||||
// checked in mgmt / update server runtime PreAuthorizeEnabledTest
|
// checked in mgmt / update server runtime PreAuthorizeEnabledTest
|
||||||
callAs(withUser("tenant_admin", SpPermission.READ_TENANT_CONFIGURATION, SpPermission.READ_GATEWAY_SECURITY_TOKEN), () -> {
|
callAs(withUser("tenant_admin", SpPermission.READ_TENANT_CONFIGURATION, SpPermission.READ_GATEWAY_SECURITY_TOKEN), () -> {
|
||||||
mvc.perform(get(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs"))
|
mvc.perform(get(SYSTEM_V1_REQUEST_MAPPING + "/configs"))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andDo(m -> System.out.println("-> 1: " + m.getResponse().getContentAsString()))
|
.andDo(m -> System.out.println("-> 1: " + m.getResponse().getContentAsString()))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@@ -319,7 +322,7 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
});
|
});
|
||||||
|
|
||||||
callAs(withUser("tenant_read", SpPermission.READ_TENANT_CONFIGURATION), () -> {
|
callAs(withUser("tenant_read", SpPermission.READ_TENANT_CONFIGURATION), () -> {
|
||||||
mvc.perform(get(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs"))
|
mvc.perform(get(SYSTEM_V1_REQUEST_MAPPING + "/configs"))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andDo(m -> System.out.println("-> 2: " + m.getResponse().getContentAsString()))
|
.andDo(m -> System.out.println("-> 2: " + m.getResponse().getContentAsString()))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
@@ -329,17 +332,18 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Long createTestDistributionSetType() {
|
private Long createTestDistributionSetType() {
|
||||||
DistributionSetType testDefaultDsType = distributionSetTypeManagement.create(DistributionSetTypeManagement.Create.builder()
|
final DistributionSetType testDefaultDsType = distributionSetTypeManagement.create(DistributionSetTypeManagement.Create.builder()
|
||||||
.key("test123").name("TestName123").description("TestDefaultDsType").build());
|
.key("test123").name("TestName123").description("TestDefaultDsType").build());
|
||||||
testDefaultDsType = distributionSetTypeManagement
|
return distributionSetTypeManagement
|
||||||
.update(DistributionSetTypeManagement.Update.builder().id(testDefaultDsType.getId()).description("TestDefaultDsType").build());
|
.update(DistributionSetTypeManagement.Update.builder().id(testDefaultDsType.getId()).description("TestDefaultDsType").build())
|
||||||
return testDefaultDsType.getId();
|
.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertDefaultDsTypeUpdateBadRequestFails(String newDefaultDsType, long oldDefaultDsType, ResultMatcher resultMatchers)
|
private void assertDefaultDsTypeUpdateBadRequestFails(
|
||||||
|
final String newDefaultDsType, final long oldDefaultDsType, final ResultMatcher resultMatchers)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}",
|
mvc.perform(put(SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", DEFAULT_DISTRIBUTION_SET_TYPE_KEY)
|
||||||
DEFAULT_DISTRIBUTION_SET_TYPE_KEY).content(newDefaultDsType)
|
.content(newDefaultDsType)
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(resultMatchers);
|
.andExpect(resultMatchers);
|
||||||
@@ -347,23 +351,25 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
"Rest endpoint for updating DefaultDistributionType failed, but actual value changed unexpectedly.");
|
"Rest endpoint for updating DefaultDistributionType failed, but actual value changed unexpectedly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertBatchConfigurationFails(Object newRolloutApprovalEnabled, Object newAuthGatewayTokenEnabled, Object newGatewayToken,
|
private void assertBatchConfigurationFails(
|
||||||
Object newDistributionSetTypeId, ResultMatcher resultMatchers) throws Exception {
|
final Object newRolloutApprovalEnabled, final Object newAuthGatewayTokenEnabled, final Object newGatewayToken,
|
||||||
|
final Object newDistributionSetTypeId, final ResultMatcher resultMatchers) throws Exception {
|
||||||
long oldDefaultDsType = getActualDefaultDsType();
|
long oldDefaultDsType = getActualDefaultDsType();
|
||||||
boolean oldRolloutApprovalConfig = (Boolean) tenantConfigurationManagement().getConfigurationValue(ROLLOUT_APPROVAL_ENABLED).getValue();
|
boolean oldRolloutApprovalConfig = (Boolean) tenantConfigurationManagement().getConfigurationValue(ROLLOUT_APPROVAL_ENABLED).getValue();
|
||||||
boolean oldAuthGatewayTokenEnabled = (Boolean) tenantConfigurationManagement().getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED)
|
boolean oldAuthGatewayTokenEnabled = (Boolean) tenantConfigurationManagement()
|
||||||
|
.getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED)
|
||||||
|
.getValue();
|
||||||
|
String oldAuthGatewayToken = (String) tenantConfigurationManagement().getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY)
|
||||||
.getValue();
|
.getValue();
|
||||||
String oldAuthGatewayToken = (String) tenantConfigurationManagement().getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY).getValue();
|
|
||||||
|
|
||||||
JSONObject configuration = new JSONObject();
|
final JSONObject configuration = new JSONObject();
|
||||||
configuration.put(ROLLOUT_APPROVAL_ENABLED, newRolloutApprovalEnabled);
|
configuration.put(ROLLOUT_APPROVAL_ENABLED, newRolloutApprovalEnabled);
|
||||||
configuration.put(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED, newAuthGatewayTokenEnabled);
|
configuration.put(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED, newAuthGatewayTokenEnabled);
|
||||||
configuration.put(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, newGatewayToken);
|
configuration.put(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, newGatewayToken);
|
||||||
configuration.put(DEFAULT_DISTRIBUTION_SET_TYPE_KEY, newDistributionSetTypeId);
|
configuration.put(DEFAULT_DISTRIBUTION_SET_TYPE_KEY, newDistributionSetTypeId);
|
||||||
String body = configuration.toString();
|
String body = configuration.toString();
|
||||||
|
|
||||||
mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs")
|
mvc.perform(put(SYSTEM_V1_REQUEST_MAPPING + "/configs").content(body).contentType(MediaType.APPLICATION_JSON))
|
||||||
.content(body).contentType(MediaType.APPLICATION_JSON))
|
|
||||||
.andDo(MockMvcResultPrinter.print())
|
.andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(resultMatchers);
|
.andExpect(resultMatchers);
|
||||||
//Check if TenantMetadata and TenantConfiguration is not changed as Batch config failed
|
//Check if TenantMetadata and TenantConfiguration is not changed as Batch config failed
|
||||||
@@ -374,7 +380,8 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg
|
|||||||
assertEquals(oldAuthGatewayTokenEnabled,
|
assertEquals(oldAuthGatewayTokenEnabled,
|
||||||
tenantConfigurationManagement().getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED).getValue(),
|
tenantConfigurationManagement().getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED).getValue(),
|
||||||
"Batch configuration update Failed, but TenantConfiguration was actually changed.");
|
"Batch configuration update Failed, but TenantConfiguration was actually changed.");
|
||||||
assertEquals(oldAuthGatewayToken, tenantConfigurationManagement().getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY).getValue(),
|
assertEquals(oldAuthGatewayToken,
|
||||||
|
tenantConfigurationManagement().getConfigurationValue(AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY).getValue(),
|
||||||
"Batch configuration update Failed, but TenantConfiguration was actually changed.");
|
"Batch configuration update Failed, but TenantConfiguration was actually changed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository;
|
package org.eclipse.hawkbit.repository;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@@ -38,25 +37,23 @@ public interface TenantConfigurationManagement extends PermissionSupport {
|
|||||||
*
|
*
|
||||||
* @param keyName the key of the configuration
|
* @param keyName the key of the configuration
|
||||||
* @param value the configuration value which will be written into the database.
|
* @param value the configuration value which will be written into the database.
|
||||||
* @return the configuration value which was just written into the database.
|
|
||||||
* @throws TenantConfigurationValidatorException if the {@code propertyType} and the value in general does not match the expected type and
|
* @throws TenantConfigurationValidatorException if the {@code propertyType} and the value in general does not match the expected type and
|
||||||
* format defined by the Key
|
* format defined by the Key
|
||||||
* @throws ConversionFailedException if the property cannot be converted to the given
|
* @throws ConversionFailedException if the property cannot be converted to the given
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(value = SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
@PreAuthorize(value = SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||||
<T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(String keyName, T value);
|
void addOrUpdateConfiguration(String keyName, Object value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds or updates a specific configuration for a specific tenant.
|
* Adds or updates a specific configuration for a specific tenant.
|
||||||
*
|
*
|
||||||
* @param configurations map containing the key - value of the configuration
|
* @param configurations map containing the key - value of the configuration
|
||||||
* @return map of all configuration values which were written into the database.
|
|
||||||
* @throws TenantConfigurationValidatorException if the {@code propertyType} and the value in general does not
|
* @throws TenantConfigurationValidatorException if the {@code propertyType} and the value in general does not
|
||||||
* match the expected type and format defined by the Key
|
* match the expected type and format defined by the Key
|
||||||
* @throws ConversionFailedException if the property cannot be converted to the given
|
* @throws ConversionFailedException if the property cannot be converted to the given
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(value = SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
@PreAuthorize(value = SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||||
<T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration(Map<String, T> configurations);
|
void addOrUpdateConfiguration(Map<String, Object> configurations);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a configuration value from the e.g. tenant overwritten configuration values or in case the tenant does not a have a specific
|
* Retrieves a configuration value from the e.g. tenant overwritten configuration values or in case the tenant does not a have a specific
|
||||||
@@ -70,7 +67,7 @@ public interface TenantConfigurationManagement extends PermissionSupport {
|
|||||||
* @throws ConversionFailedException if the property cannot be converted to the given {@code propertyType}
|
* @throws ConversionFailedException if the property cannot be converted to the given {@code propertyType}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(value = SpringEvalExpressions.HAS_READ_REPOSITORY)
|
@PreAuthorize(value = SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||||
<T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(String keyName);
|
<T> TenantConfigurationValue<T> getConfigurationValue(String keyName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a configuration value from the e.g. tenant overwritten configuration values or in case the tenant does not a have a specific
|
* Retrieves a configuration value from the e.g. tenant overwritten configuration values or in case the tenant does not a have a specific
|
||||||
@@ -86,7 +83,7 @@ public interface TenantConfigurationManagement extends PermissionSupport {
|
|||||||
* @throws ConversionFailedException if the property cannot be converted to the given {@code propertyType}
|
* @throws ConversionFailedException if the property cannot be converted to the given {@code propertyType}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(value = SpringEvalExpressions.HAS_READ_REPOSITORY)
|
@PreAuthorize(value = SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||||
<T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(String keyName, Class<T> propertyType);
|
<T> TenantConfigurationValue<T> getConfigurationValue(String keyName, Class<T> propertyType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a specific configuration for the current tenant. Does nothing in case there is no tenant specific configuration value.
|
* Deletes a specific configuration for the current tenant. Does nothing in case there is no tenant specific configuration value.
|
||||||
|
|||||||
@@ -9,9 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.model;
|
package org.eclipse.hawkbit.repository.model;
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -22,10 +19,7 @@ import lombok.Data;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
public final class TenantConfigurationValue<T extends Serializable> implements Serializable {
|
public final class TenantConfigurationValue<T> {
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private T value;
|
private T value;
|
||||||
private Long lastModifiedAt;
|
private Long lastModifiedAt;
|
||||||
|
|||||||
@@ -27,17 +27,17 @@ import org.springframework.core.Ordered;
|
|||||||
public class ExceptionMappingAspectHandler implements Ordered {
|
public class ExceptionMappingAspectHandler implements Ordered {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Catches exceptions of the {@link TransactionManager} and wrap them to custom exceptions.
|
* Catches exceptions the {@link TransactionManager} and wrap them to custom exceptions.
|
||||||
*
|
*
|
||||||
* @param ex the thrown and catched exception
|
* @param e the thrown and caught exception
|
||||||
* @throws Throwable
|
* @throws Throwable the mapped exception
|
||||||
*/
|
*/
|
||||||
@AfterThrowing(pointcut = "execution( * org.eclipse.hawkbit.repository.jpa.management.*Management.*(..))", throwing = "ex")
|
@AfterThrowing(pointcut = "execution( * org.eclipse.hawkbit.repository.jpa.management.*Management.*(..))", throwing = "e")
|
||||||
// Exception for squid:S00112, squid:S1162
|
// Exception for squid:S00112, squid:S1162
|
||||||
// It is a AspectJ proxy which deals with exceptions.
|
// It is a AspectJ proxy which deals with exceptions.
|
||||||
@SuppressWarnings({ "squid:S00112", "squid:S1162" })
|
@SuppressWarnings({ "squid:S00112", "squid:S1162" })
|
||||||
public void catchAndWrapJpaExceptionsService(final Exception ex) throws Throwable {
|
public void catchAndWrapJpaExceptionsService(final Exception e) throws Throwable {
|
||||||
throw ExceptionMapper.map(ex);
|
throw ExceptionMapper.map(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,19 +16,17 @@ import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationPrope
|
|||||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.POLLING_TIME;
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.POLLING_TIME;
|
||||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.hawkbit.auth.SpPermission;
|
import org.eclipse.hawkbit.auth.SpPermission;
|
||||||
import org.eclipse.hawkbit.context.AccessContext;
|
import org.eclipse.hawkbit.context.AccessContext;
|
||||||
@@ -95,7 +93,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(final ContextRefreshedEvent event) {
|
public void onApplicationEvent(@NonNull final ContextRefreshedEvent event) {
|
||||||
// Sets the proxy / bean from the context in order to be used via proxy and onore things like @PreAuthorize and @Transactional
|
// Sets the proxy / bean from the context in order to be used via proxy and onore things like @PreAuthorize and @Transactional
|
||||||
TenantConfigHelper.setTenantConfigurationManagement(applicationContext.getBean(JpaTenantConfigurationManagement.class));
|
TenantConfigHelper.setTenantConfigurationManagement(applicationContext.getBean(JpaTenantConfigurationManagement.class));
|
||||||
}
|
}
|
||||||
@@ -104,25 +102,25 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(final String keyName, final T value) {
|
public void addOrUpdateConfiguration(final String keyName, final Object value) {
|
||||||
return addOrUpdateConfiguration0(Map.of(keyName, value)).values().iterator().next();
|
addOrUpdateConfiguration0(Map.of(keyName, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public <T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration(final Map<String, T> configurations) {
|
public void addOrUpdateConfiguration(final Map<String, Object> configurations) {
|
||||||
return addOrUpdateConfiguration0(configurations);
|
addOrUpdateConfiguration0(configurations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(final String keyName) {
|
public <T> TenantConfigurationValue<T> getConfigurationValue(final String keyName) {
|
||||||
return getConfigurationValue0(keyName, null);
|
return getConfigurationValue0(keyName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(final String keyName, final Class<T> propertyType) {
|
public <T> TenantConfigurationValue<T> getConfigurationValue(final String keyName, final Class<T> propertyType) {
|
||||||
return getConfigurationValue0(keyName, propertyType);
|
return getConfigurationValue0(keyName, propertyType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,56 +176,47 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkAccess(final String keyName) {
|
private void checkAccess(final String keyName) {
|
||||||
if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(keyName)) {
|
if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(keyName)
|
||||||
if (!AccessContext.isCurrentThreadSystemCode() && !SpPermission.hasPermission(READ_GATEWAY_SECURITY_TOKEN)) {
|
&& !AccessContext.isCurrentThreadSystemCode() && !SpPermission.hasPermission(READ_GATEWAY_SECURITY_TOKEN)) {
|
||||||
throw new InsufficientPermissionException(
|
throw new InsufficientPermissionException("Can't read gateway security token! " + READ_GATEWAY_SECURITY_TOKEN + " is required!");
|
||||||
"Can't read gateway security token! " + READ_GATEWAY_SECURITY_TOKEN + " is required!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
private void addOrUpdateConfiguration0(final Map<String, Object> configurations) {
|
||||||
private <T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration0(final Map<String, T> configurations) {
|
tenantConfigurationRepository.saveAll(configurations.entrySet().stream().map(e -> {
|
||||||
final List<JpaTenantConfiguration> configurationList = new ArrayList<>();
|
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(e.getKey());
|
||||||
configurations.forEach((keyName, value) -> {
|
|
||||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(keyName);
|
|
||||||
|
|
||||||
final Class<?> targetType = configurationKey.getDataType();
|
final Class<?> targetType = configurationKey.getDataType();
|
||||||
Object convertedValue = getConvertedValue(value, targetType);
|
final Object convertedValue;
|
||||||
validateConfigurationValue(value, configurationKey, convertedValue);
|
try {
|
||||||
|
convertedValue = CONVERSION_SERVICE.convert(e.getValue(), targetType);
|
||||||
|
} catch (final ConversionException | IllegalArgumentException ex) {
|
||||||
|
throw new TenantConfigurationValidatorException(String.format(
|
||||||
|
"Cannot convert the value %s of type %s to the type %s defined by the configuration key.",
|
||||||
|
e.getValue(), e.getValue().getClass(), targetType));
|
||||||
|
}
|
||||||
|
final String valueToString = Optional.ofNullable(convertedValue)
|
||||||
|
.map(Object::toString)
|
||||||
|
.orElse(null);
|
||||||
|
validateConfigurationValue(configurationKey, convertedValue);
|
||||||
|
|
||||||
JpaTenantConfiguration tenantConfiguration = tenantConfigurationRepository.findByKey(configurationKey.getKeyName());
|
JpaTenantConfiguration tenantConfiguration = tenantConfigurationRepository.findByKey(configurationKey.getKeyName());
|
||||||
if (tenantConfiguration == null) {
|
if (tenantConfiguration == null) {
|
||||||
tenantConfiguration = new JpaTenantConfiguration(configurationKey.getKeyName(), convertedValue.toString());
|
tenantConfiguration = new JpaTenantConfiguration(configurationKey.getKeyName(), valueToString);
|
||||||
} else {
|
} else {
|
||||||
tenantConfiguration.setValue(convertedValue.toString());
|
tenantConfiguration.setValue(valueToString);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertValueChangeIsAllowed(keyName, tenantConfiguration);
|
assertValueChangeIsAllowed(e.getKey(), tenantConfiguration);
|
||||||
configurationList.add(tenantConfiguration);
|
return tenantConfiguration;
|
||||||
});
|
}).toList());
|
||||||
return tenantConfigurationRepository.saveAll(configurationList).
|
|
||||||
stream().
|
|
||||||
collect(Collectors.toMap(
|
|
||||||
JpaTenantConfiguration::getKey,
|
|
||||||
updatedTenantConfiguration -> TenantConfigurationValue.<T> builder()
|
|
||||||
.global(false)
|
|
||||||
.createdBy(updatedTenantConfiguration.getCreatedBy())
|
|
||||||
.createdAt(updatedTenantConfiguration.getCreatedAt())
|
|
||||||
.lastModifiedAt(updatedTenantConfiguration.getLastModifiedAt())
|
|
||||||
.lastModifiedBy(updatedTenantConfiguration.getLastModifiedBy())
|
|
||||||
.value(CONVERSION_SERVICE.convert(
|
|
||||||
updatedTenantConfiguration.getValue(),
|
|
||||||
(Class<T>) configurations.get(updatedTenantConfiguration.getKey()).getClass()))
|
|
||||||
.build()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends Serializable> void validateConfigurationValue(final T value, final TenantConfigurationKey configurationKey,
|
private void validateConfigurationValue(final TenantConfigurationKey configurationKey, final Object value) {
|
||||||
final Object convertedValue) {
|
configurationKey.validate(value, applicationContext);
|
||||||
configurationKey.validate(convertedValue, applicationContext);
|
|
||||||
// additional validation for specific configuration keys
|
// additional validation for specific configuration keys
|
||||||
if (POLLING_TIME.equals(configurationKey.getKeyName())) {
|
if (POLLING_TIME.equals(configurationKey.getKeyName())) {
|
||||||
final PollingTime pollingTime = new PollingTime(value.toString());
|
final PollingTime pollingTime = new PollingTime(String.valueOf(value));
|
||||||
if (!ObjectUtils.isEmpty(pollingTime.getOverrides())) {
|
if (!ObjectUtils.isEmpty(pollingTime.getOverrides())) {
|
||||||
// validate that the QL strings are valid RSQL queries,
|
// validate that the QL strings are valid RSQL queries,
|
||||||
// nevertheless always when parse them we shall be prepared to catch exceptions if the parsers
|
// nevertheless always when parse them we shall be prepared to catch exceptions if the parsers
|
||||||
@@ -237,62 +226,36 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T extends Serializable> Object getConvertedValue(final T value, final Class<?> targetType) {
|
|
||||||
Object convertedValue = value;
|
|
||||||
if (!targetType.isAssignableFrom(value.getClass())) {
|
|
||||||
try {
|
|
||||||
// if not assignable and it is a number - try conversion
|
|
||||||
// for example tries to assign Integer to Long
|
|
||||||
if (value instanceof Number number && Number.class.isAssignableFrom(targetType)) {
|
|
||||||
log.debug("Type {} not assignable from {} . Will try conversion.", targetType, value.getClass());
|
|
||||||
convertedValue = CONVERSION_SERVICE.convert(number, targetType);
|
|
||||||
if (convertedValue == null) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
String.format("Failed to convert %s. Convertor returned null as a result", value));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
String.format("Value %s is not a Number but %s and cannot perform conversion converted.", value, value.getClass()));
|
|
||||||
}
|
|
||||||
} catch (final ConversionException | IllegalArgumentException ex) {
|
|
||||||
throw new TenantConfigurationValidatorException(String.format(
|
|
||||||
"Cannot convert the value %s of type %s to the type %s defined by the configuration key.",
|
|
||||||
value, value.getClass(), targetType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return convertedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue0(final String keyName, final Class<T> propertyType) {
|
private <T> TenantConfigurationValue<T> getConfigurationValue0(final String keyName, final Class<T> propertyType) {
|
||||||
checkAccess(keyName);
|
checkAccess(keyName);
|
||||||
|
|
||||||
final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(keyName);
|
final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(keyName);
|
||||||
if (propertyType != null) {
|
final Class<T> type;
|
||||||
|
if (propertyType == null) {
|
||||||
|
type = (Class<T>) key.getDataType();
|
||||||
|
} else {
|
||||||
validateTenantConfigurationDataType(key, propertyType);
|
validateTenantConfigurationDataType(key, propertyType);
|
||||||
|
type = propertyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
final TenantConfiguration tenantConfiguration = TenantAwareCacheManager.getInstance().getCache(CACHE_TENANT_CONFIGURATION_NAME)
|
final TenantConfiguration tenantConfiguration = TenantAwareCacheManager.getInstance()
|
||||||
|
.getCache(CACHE_TENANT_CONFIGURATION_NAME)
|
||||||
.get(key.getKeyName(), () -> tenantConfigurationRepository.findByKey(key.getKeyName()));
|
.get(key.getKeyName(), () -> tenantConfigurationRepository.findByKey(key.getKeyName()));
|
||||||
return buildTenantConfigurationValueByKey(key, propertyType == null ? (Class<T>) key.getDataType() : propertyType, tenantConfiguration);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends Serializable> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(
|
|
||||||
final TenantConfigurationKey configurationKey, final Class<T> propertyType, final TenantConfiguration tenantConfiguration) {
|
|
||||||
if (tenantConfiguration != null) {
|
if (tenantConfiguration != null) {
|
||||||
return TenantConfigurationValue.<T> builder().global(false)
|
return TenantConfigurationValue.<T> builder()
|
||||||
|
.global(false)
|
||||||
.createdBy(tenantConfiguration.getCreatedBy())
|
.createdBy(tenantConfiguration.getCreatedBy())
|
||||||
.createdAt(tenantConfiguration.getCreatedAt())
|
.createdAt(tenantConfiguration.getCreatedAt())
|
||||||
.lastModifiedAt(tenantConfiguration.getLastModifiedAt())
|
.lastModifiedAt(tenantConfiguration.getLastModifiedAt())
|
||||||
.lastModifiedBy(tenantConfiguration.getLastModifiedBy())
|
.lastModifiedBy(tenantConfiguration.getLastModifiedBy())
|
||||||
.value(CONVERSION_SERVICE.convert(tenantConfiguration.getValue(), propertyType)).build();
|
.value(CONVERSION_SERVICE.convert(tenantConfiguration.getValue(), type))
|
||||||
} else if (configurationKey.getDefaultValue() != null) {
|
.build();
|
||||||
return TenantConfigurationValue.<T> builder().global(true)
|
} else if (key.getDefaultValue() != null) {
|
||||||
.createdBy(null)
|
return TenantConfigurationValue.<T> builder()
|
||||||
.createdAt(null)
|
.global(true)
|
||||||
.lastModifiedAt(null)
|
.value(getGlobalConfigurationValue0(key.getKeyName(), type))
|
||||||
.lastModifiedBy(null)
|
.build();
|
||||||
.value(getGlobalConfigurationValue0(configurationKey.getKeyName(), propertyType)).build();
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -342,7 +305,9 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
|||||||
|
|
||||||
private void assertAutoCloseValueChange(final String key) {
|
private void assertAutoCloseValueChange(final String key) {
|
||||||
if (REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED.equals(key)
|
if (REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED.equals(key)
|
||||||
&& Boolean.TRUE.equals(getConfigurationValue0(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue())) {
|
&& Boolean.TRUE.equals(Optional.ofNullable(getConfigurationValue0(MULTI_ASSIGNMENTS_ENABLED, Boolean.class))
|
||||||
|
.map(TenantConfigurationValue::getValue)
|
||||||
|
.orElse(null))) {
|
||||||
log.debug("The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.", key);
|
log.debug("The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.", key);
|
||||||
throw new TenantConfigurationValueChangeNotAllowedException();
|
throw new TenantConfigurationValueChangeNotAllowedException();
|
||||||
}
|
}
|
||||||
@@ -350,7 +315,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
|||||||
|
|
||||||
private void assertBatchAssignmentValueChange(final String key, final JpaTenantConfiguration valueChange) {
|
private void assertBatchAssignmentValueChange(final String key, final JpaTenantConfiguration valueChange) {
|
||||||
if (BATCH_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
|
if (BATCH_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
|
||||||
JpaTenantConfiguration multiConfig = tenantConfigurationRepository.findByKey(MULTI_ASSIGNMENTS_ENABLED);
|
final JpaTenantConfiguration multiConfig = tenantConfigurationRepository.findByKey(MULTI_ASSIGNMENTS_ENABLED);
|
||||||
if (multiConfig != null && Boolean.parseBoolean(multiConfig.getValue())) {
|
if (multiConfig != null && Boolean.parseBoolean(multiConfig.getValue())) {
|
||||||
log.debug(
|
log.debug(
|
||||||
"The Batch-Assignments '{}' feature cannot be enabled as it contradicts with the Multi-Assignments feature, which is already enabled .",
|
"The Batch-Assignments '{}' feature cannot be enabled as it contradicts with the Multi-Assignments feature, which is already enabled .",
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ class AutoActionCleanupTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(actionRepository.count()).isEqualTo(3);
|
assertThat(actionRepository.count()).isEqualTo(3);
|
||||||
|
|
||||||
// wait for expiry to elapse
|
// wait for expiry to elapse
|
||||||
Thread.sleep(800);
|
waitMillis(800);
|
||||||
|
|
||||||
autoActionCleanup.run();
|
autoActionCleanup.run();
|
||||||
|
|
||||||
|
|||||||
@@ -77,16 +77,19 @@ class DistributedLockTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
LockRepository lockRepository0(final DataSource dataSource, final LockProperties lockProperties, final PlatformTransactionManager txManager) {
|
LockRepository lockRepository0(final DataSource dataSource, final LockProperties lockProperties,
|
||||||
|
final PlatformTransactionManager txManager) {
|
||||||
return lockRepository(dataSource, lockProperties, txManager);
|
return lockRepository(dataSource, lockProperties, txManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
LockRepository lockRepository1(final DataSource dataSource, final LockProperties lockProperties, final PlatformTransactionManager txManager) {
|
LockRepository lockRepository1(final DataSource dataSource, final LockProperties lockProperties,
|
||||||
|
final PlatformTransactionManager txManager) {
|
||||||
return lockRepository(dataSource, lockProperties, txManager);
|
return lockRepository(dataSource, lockProperties, txManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LockRepository lockRepository(final DataSource dataSource, final LockProperties lockProperties, final PlatformTransactionManager txManager) {
|
private LockRepository lockRepository(final DataSource dataSource, final LockProperties lockProperties,
|
||||||
|
final PlatformTransactionManager txManager) {
|
||||||
final DefaultLockRepository repository = new DistributedLockRepository(dataSource, lockProperties, txManager);
|
final DefaultLockRepository repository = new DistributedLockRepository(dataSource, lockProperties, txManager);
|
||||||
repository.setPrefix("SP_");
|
repository.setPrefix("SP_");
|
||||||
return repository;
|
return repository;
|
||||||
@@ -96,7 +99,7 @@ class DistributedLockTest extends AbstractJpaIntegrationTest {
|
|||||||
/**
|
/**
|
||||||
* Test to verify that lock is kept while ping runs
|
* Test to verify that lock is kept while ping runs
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"java:S2925"})
|
@SuppressWarnings({ "java:S2925" })
|
||||||
@Test
|
@Test
|
||||||
void keepLockAlive() {
|
void keepLockAlive() {
|
||||||
final LockRegistry lockRegistry0 = new JdbcLockRegistry(lockRepository0);
|
final LockRegistry lockRegistry0 = new JdbcLockRegistry(lockRepository0);
|
||||||
@@ -140,13 +143,7 @@ class DistributedLockTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(lock01.tryLock()).isFalse();
|
assertThat(lock01.tryLock()).isFalse();
|
||||||
assertThat(lockRepository1.isAcquired(path0)).isFalse(); // check db state
|
assertThat(lockRepository1.isAcquired(path0)).isFalse(); // check db state
|
||||||
|
|
||||||
try {
|
waitMillis(Math.min(1, lockProperties.getTtl() / 4));
|
||||||
Thread.sleep(Math.min(1, lockProperties.getTtl() / 4));
|
|
||||||
} catch (final InterruptedException e) {
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (final AssertionError e) {
|
} catch (final AssertionError e) {
|
||||||
log.error("lockRepository1 has locked lockKey0 which has to be in lockRepository0 possession!", e);
|
log.error("lockRepository1 has locked lockKey0 which has to be in lockRepository0 possession!", e);
|
||||||
@@ -183,13 +180,7 @@ class DistributedLockTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
waitMillis(Math.min(1, lockProperties.getTtl() / 4));
|
||||||
Thread.sleep(Math.min(1, lockProperties.getTtl() / 4));
|
|
||||||
} catch (final InterruptedException e) {
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import java.time.Duration;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import lombok.NonNull;
|
||||||
|
import org.awaitility.Awaitility;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.repository.exception.InvalidTenantConfigurationKeyException;
|
import org.eclipse.hawkbit.repository.exception.InvalidTenantConfigurationKeyException;
|
||||||
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
|
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
|
||||||
@@ -28,6 +30,7 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.T
|
|||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.opentest4j.AssertionFailedError;
|
||||||
import org.springframework.context.EnvironmentAware;
|
import org.springframework.context.EnvironmentAware;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
@@ -48,7 +51,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setEnvironment(final Environment env) {
|
public void setEnvironment(@NonNull final Environment env) {
|
||||||
this.env = env;
|
this.env = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,12 +89,20 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
|||||||
tenantConfigurationManagement.addOrUpdateConfiguration(
|
tenantConfigurationManagement.addOrUpdateConfiguration(
|
||||||
TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, newConfigurationValue2);
|
TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, newConfigurationValue2);
|
||||||
|
|
||||||
|
// sometimes it reads old value, maybe if read too early. wait to settle up?
|
||||||
|
waitMillis(100);
|
||||||
|
|
||||||
// verify that new configuration value is used
|
// verify that new configuration value is used
|
||||||
final TenantConfigurationValue<String> updatedConfigurationValue2 = tenantConfigurationManagement
|
final TenantConfigurationValue<String> updatedConfigurationValue2 = tenantConfigurationManagement
|
||||||
.getConfigurationValue(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class);
|
.getConfigurationValue(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class);
|
||||||
|
|
||||||
assertThat(updatedConfigurationValue2.isGlobal()).isFalse();
|
assertThat(updatedConfigurationValue2.isGlobal()).isFalse();
|
||||||
|
try {
|
||||||
assertThat(updatedConfigurationValue2.getValue()).isEqualTo(newConfigurationValue2);
|
assertThat(updatedConfigurationValue2.getValue()).isEqualTo(newConfigurationValue2);
|
||||||
|
} catch (final AssertionFailedError e) {
|
||||||
|
Awaitility.await().atMost(Duration.ofSeconds(20)).pollInterval(Duration.ofMillis(100))
|
||||||
|
.untilAsserted(() -> assertThat(updatedConfigurationValue2.getValue()).isEqualTo(newConfigurationValue2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,7 +128,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void batchUpdateTenantSpecificConfiguration() {
|
void batchUpdateTenantSpecificConfiguration() {
|
||||||
final Map<String, Serializable> configuration = new HashMap<>() {{
|
final Map<String, Object> configuration = new HashMap<>() {{
|
||||||
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
|
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
|
||||||
put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, true);
|
put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, true);
|
||||||
}};
|
}};
|
||||||
@@ -165,7 +176,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void batchWrongTenantConfigurationValueTypeThrowsException() {
|
void batchWrongTenantConfigurationValueTypeThrowsException() {
|
||||||
final Map<String, Serializable> configuration = new HashMap<>() {{
|
final Map<String, Object> configuration = new HashMap<>() {{
|
||||||
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
|
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
|
||||||
put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, true);
|
put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, true);
|
||||||
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED, "wrong");
|
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED, "wrong");
|
||||||
@@ -216,12 +227,19 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
|||||||
* Test that an Exception is thrown, when an integer is stored but a string expected.
|
* Test that an Exception is thrown, when an integer is stored but a string expected.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void storesIntegerWhenStringIsExpected() {
|
void storesStringWhenIntegerIsExpected() {
|
||||||
final String configKey = TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
|
final String configKey = TenantConfigurationKey.ACTION_CLEANUP_ON_QUOTA_HIT_PERCENTAGE;
|
||||||
final Integer wrongDatType = 123;
|
final String wrongDataType = "123f";
|
||||||
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDatType))
|
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataType))
|
||||||
.as("Should not have worked as integer is not a string")
|
.as("Should not have worked as integer is not a string")
|
||||||
.isInstanceOf(TenantConfigurationValidatorException.class);
|
.isInstanceOf(TenantConfigurationValidatorException.class);
|
||||||
|
|
||||||
|
final Integer correctDataType = 123;
|
||||||
|
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, String.valueOf(correctDataType));
|
||||||
|
assertThat(tenantConfigurationManagement.getConfigurationValue(configKey, Integer.class).getValue()).isEqualTo(correctDataType);
|
||||||
|
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, correctDataType);
|
||||||
|
assertThat(tenantConfigurationManagement.getConfigurationValue(configKey, Integer.class).getValue()).isEqualTo(correctDataType);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,10 +258,9 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
|||||||
* Test that an Exception is thrown, when an integer is stored as PollingTime.
|
* Test that an Exception is thrown, when an integer is stored as PollingTime.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void storesIntegerWhenPollingIntervalIsExpected() {
|
void storesBadPollingIntervalIsExpected() {
|
||||||
final String configKey = TenantConfigurationKey.POLLING_TIME;
|
final String configKey = TenantConfigurationKey.POLLING_TIME;
|
||||||
final Integer wrongDataType = 123;
|
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, "wrongDataType"))
|
||||||
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataType))
|
|
||||||
.as("Should not have worked as integer is not a time field")
|
.as("Should not have worked as integer is not a time field")
|
||||||
.isInstanceOf(TenantConfigurationValidatorException.class);
|
.isInstanceOf(TenantConfigurationValidatorException.class);
|
||||||
}
|
}
|
||||||
@@ -350,16 +367,6 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
|||||||
Assertions.assertEquals(2592000000L, autoCleanupDaysInMs);
|
Assertions.assertEquals(2592000000L, autoCleanupDaysInMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void throwExceptionIfTryingToConvertOtherValueThanNumber() {
|
|
||||||
final String configKey = TenantConfigurationKey.ACTION_CLEANUP_AUTO_EXPIRY;
|
|
||||||
// set auto cleanup for 1 day in String ms
|
|
||||||
assertThatThrownBy(() ->
|
|
||||||
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, "86400000"))
|
|
||||||
.as("Cannot convert the value 86400000 of type String to the type Long defined by the configuration key.")
|
|
||||||
.isInstanceOf(TenantConfigurationValidatorException.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Duration getDurationByTimeValues(final long hours, final long minutes, final long seconds) {
|
private static Duration getDurationByTimeValues(final long hours, final long minutes, final long seconds) {
|
||||||
return Duration.ofHours(hours).plusMinutes(minutes).plusSeconds(seconds);
|
return Duration.ofHours(hours).plusMinutes(minutes).plusSeconds(seconds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.context.AccessContext;
|
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
|
|||||||
@@ -482,13 +482,17 @@ public abstract class AbstractIntegrationTest {
|
|||||||
protected void waitNextMillis() {
|
protected void waitNextMillis() {
|
||||||
final long createTime = System.currentTimeMillis();
|
final long createTime = System.currentTimeMillis();
|
||||||
while (System.currentTimeMillis() == createTime) {
|
while (System.currentTimeMillis() == createTime) {
|
||||||
|
waitMillis(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void waitMillis(final long millis) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1);
|
Thread.sleep(millis);
|
||||||
} catch (final InterruptedException e) {
|
} catch (final InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("java:S4042")
|
@SuppressWarnings("java:S4042")
|
||||||
private static File createTempDir() {
|
private static File createTempDir() {
|
||||||
|
|||||||
Reference in New Issue
Block a user