New repository quota on messages per ActionStatus (#531)
* New quotaexception for repository quota hit on DDI. Added actionstatus messages quota. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Remove special log handling on the quota exception. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Raise time for slow machines. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Update allure to get rid of log spam in unit tests. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Typos fixed. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
@@ -103,12 +103,7 @@ public enum SpServerError {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_ACTION_STATUS_TO_MANY_ENTRIES("hawkbit.server.error.action.status.tooManyEntries", "Too many status entries have been inserted."),
|
SP_QUOTA_EXCEEDED("hawkbit.server.error.quota.tooManyEntries", "Too many entries have been inserted."),
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
SP_ATTRIBUTES_TO_MANY_ENTRIES("hawkbit.server.error.target.attributes.tooManyEntries", "Too many attribute entries have been inserted."),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* error message, which describes that the action can not be canceled cause
|
* error message, which describes that the action can not be canceled cause
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import java.util.List;
|
|||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Size;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
@@ -32,7 +31,6 @@ public class DdiStatus {
|
|||||||
@Valid
|
@Valid
|
||||||
private final DdiResult result;
|
private final DdiResult result;
|
||||||
|
|
||||||
@Size(min = 0, max = 50)
|
|
||||||
private final List<String> details;
|
private final List<String> details;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
|||||||
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
|
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
|
||||||
|
|
||||||
final DistributionSetAssignmentResult result = deploymentManagement.assignDistributionSet(ds.getId(),
|
final DistributionSetAssignmentResult result = deploymentManagement.assignDistributionSet(ds.getId(),
|
||||||
ActionType.TIMEFORCED, System.currentTimeMillis() + 1_000, Arrays.asList(target.getControllerId()));
|
ActionType.TIMEFORCED, System.currentTimeMillis() + 2_000, Arrays.asList(target.getControllerId()));
|
||||||
|
|
||||||
final Action action = deploymentManagement
|
final Action action = deploymentManagement
|
||||||
.findActiveActionsByTarget(PAGE, result.getAssignedEntity().get(0).getControllerId()).getContent()
|
.findActiveActionsByTarget(PAGE, result.getAssignedEntity().get(0).getControllerId()).getContent()
|
||||||
@@ -255,7 +255,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
|||||||
+ "/controller/v1/4712/deploymentBase/" + action.getId());
|
+ "/controller/v1/4712/deploymentBase/" + action.getId());
|
||||||
|
|
||||||
// After the time is over we should see a new etag
|
// After the time is over we should see a new etag
|
||||||
TimeUnit.MILLISECONDS.sleep(1_000);
|
TimeUnit.MILLISECONDS.sleep(2_000);
|
||||||
|
|
||||||
mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
|
mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
@@ -539,7 +539,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("The server protects itself against to many feedback upload attempts. The test verfies that "
|
@Description("The server protects itself against to many feedback upload attempts. The test verfies that "
|
||||||
+ "it is not possible to exceed the configured maximum number of feedback uplods.")
|
+ "it is not possible to exceed the configured maximum number of feedback uplods.")
|
||||||
public void toMuchDeplomentActionFeedback() throws Exception {
|
public void tooMuchDeplomentActionFeedback() throws Exception {
|
||||||
final Target target = testdataFactory.createTarget("4712");
|
final Target target = testdataFactory.createTarget("4712");
|
||||||
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
||||||
|
|
||||||
@@ -562,6 +562,31 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
|||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isForbidden());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("The server protects itself against too large feedback bodies. The test verifies that "
|
||||||
|
+ "it is not possible to exceed the configured maximum number of feedback details.")
|
||||||
|
public void tooMuchDeploymentActionMessagesInFeedback() throws Exception {
|
||||||
|
final Target target = testdataFactory.createTarget("4712");
|
||||||
|
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
||||||
|
|
||||||
|
assignDistributionSet(ds.getId(), "4712");
|
||||||
|
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
|
||||||
|
.get(0);
|
||||||
|
|
||||||
|
final List<String> messages = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 51; i++) {
|
||||||
|
messages.add(String.valueOf(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), "proceeding", "none",
|
||||||
|
messages);
|
||||||
|
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
|
||||||
|
tenantAware.getCurrentTenant()).content(feedback).contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.accept(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Multiple uploads of deployment status feedback to the server.")
|
@Description("Multiple uploads of deployment status feedback to the server.")
|
||||||
public void multipleDeplomentActionFeedback() throws Exception {
|
public void multipleDeplomentActionFeedback() throws Exception {
|
||||||
|
|||||||
@@ -15,9 +15,8 @@ import javax.validation.ConstraintViolationException;
|
|||||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
|
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||||
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
|
|
||||||
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler;
|
import org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler;
|
||||||
@@ -72,7 +71,7 @@ public class DelayedRequeueExceptionStrategy extends ConditionalRejectingErrorHa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean quotaHit(final Throwable cause) {
|
private boolean quotaHit(final Throwable cause) {
|
||||||
return cause instanceof TooManyStatusEntriesException || cause instanceof ToManyAttributeEntriesException;
|
return cause instanceof QuotaExceededException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doesNotExist(final Throwable cause) {
|
private boolean doesNotExist(final Throwable cause) {
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
|
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
|
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
@@ -52,9 +51,9 @@ public interface ControllerManagement {
|
|||||||
* @throws EntityAlreadyExistsException
|
* @throws EntityAlreadyExistsException
|
||||||
* if a given entity already exists
|
* if a given entity already exists
|
||||||
*
|
*
|
||||||
* @throws TooManyStatusEntriesException
|
* @throws QuotaExceededException
|
||||||
* if more than the allowed number of status entries are
|
* if more than the allowed number of status entries or messages
|
||||||
* inserted
|
* per entry are inserted
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if given action does not exist
|
* if given action does not exist
|
||||||
*
|
*
|
||||||
@@ -87,9 +86,9 @@ public interface ControllerManagement {
|
|||||||
*
|
*
|
||||||
* @return created {@link ActionStatus} entity
|
* @return created {@link ActionStatus} entity
|
||||||
*
|
*
|
||||||
* @throws TooManyStatusEntriesException
|
* @throws QuotaExceededException
|
||||||
* if more than the allowed number of status entries are
|
* if more than the allowed number of status entries or messages
|
||||||
* inserted
|
* per entry are inserted
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if given action does not exist
|
* if given action does not exist
|
||||||
*/
|
*/
|
||||||
@@ -106,9 +105,9 @@ public interface ControllerManagement {
|
|||||||
*
|
*
|
||||||
* @throws EntityAlreadyExistsException
|
* @throws EntityAlreadyExistsException
|
||||||
* if a given entity already exists
|
* if a given entity already exists
|
||||||
* @throws TooManyStatusEntriesException
|
* @throws QuotaExceededException
|
||||||
* if more than the allowed number of status entries are
|
* if more than the allowed number of status entries or messages
|
||||||
* inserted
|
* per entry are inserted
|
||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if action status not exist
|
* if action status not exist
|
||||||
@@ -267,8 +266,8 @@ public interface ControllerManagement {
|
|||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if target that has to be updated could not be found
|
* if target that has to be updated could not be found
|
||||||
* @throws ToManyAttributeEntriesException
|
* @throws QuotaExceededException
|
||||||
* if maximum
|
* if maximum number of attribzes per target is exceeded
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||||
Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map<String, String> attributes);
|
Target updateControllerAttributes(@NotEmpty String controllerId, @NotNull Map<String, String> attributes);
|
||||||
|
|||||||
@@ -36,4 +36,11 @@ public interface QuotaManagement {
|
|||||||
*/
|
*/
|
||||||
int getMaxRolloutGroupsPerRollout();
|
int getMaxRolloutGroupsPerRollout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return maximum number of
|
||||||
|
* {@link ControllerManagement#getActionHistoryMessages(Long, int)}
|
||||||
|
* for an individual {@link ActionStatus}.
|
||||||
|
*/
|
||||||
|
int getMaxMessagesPerActionStatus();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.repository.exception;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||||
|
import org.eclipse.hawkbit.exception.SpServerError;
|
||||||
|
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown if too many entries are added to repository.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class QuotaExceededException extends AbstractServerRtException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new QuotaExceededException with
|
||||||
|
* {@link SpServerError#SP_QUOTA_EXCEEDED} error.
|
||||||
|
*/
|
||||||
|
public QuotaExceededException() {
|
||||||
|
super(SpServerError.SP_QUOTA_EXCEEDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cause
|
||||||
|
* for the exception
|
||||||
|
*/
|
||||||
|
public QuotaExceededException(final Throwable cause) {
|
||||||
|
super(SpServerError.SP_QUOTA_EXCEEDED, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param type
|
||||||
|
* that hit quota
|
||||||
|
* @param inserted
|
||||||
|
* cause for the hit
|
||||||
|
* @param quota
|
||||||
|
* that is defined by the repository
|
||||||
|
*/
|
||||||
|
public QuotaExceededException(final Class<? extends BaseEntity> type, final long inserted, final int quota) {
|
||||||
|
this(type.getSimpleName(), inserted, quota);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* that hit quota
|
||||||
|
* @param inserted
|
||||||
|
* cause for the hit
|
||||||
|
* @param quota
|
||||||
|
* that is defined by the repository
|
||||||
|
*/
|
||||||
|
public QuotaExceededException(final String type, final long inserted, final int quota) {
|
||||||
|
super("Request contains too many entries of {" + type + "}. {" + inserted + "} is bejond the permitted {"
|
||||||
|
+ quota + "}.", SpServerError.SP_QUOTA_EXCEEDED);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
|
||||||
*
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*/
|
|
||||||
package org.eclipse.hawkbit.repository.exception;
|
|
||||||
|
|
||||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
|
||||||
import org.eclipse.hawkbit.exception.SpServerError;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown if too many status entries have been inserted.
|
|
||||||
*/
|
|
||||||
public final class ToManyAttributeEntriesException extends AbstractServerRtException {
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new FileUploadFailedException with
|
|
||||||
* {@link SpServerError#SP_ATTRIBUTES_TO_MANY_ENTRIES} error.
|
|
||||||
*/
|
|
||||||
public ToManyAttributeEntriesException() {
|
|
||||||
super(SpServerError.SP_ATTRIBUTES_TO_MANY_ENTRIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param cause
|
|
||||||
* for the exception
|
|
||||||
*/
|
|
||||||
public ToManyAttributeEntriesException(final Throwable cause) {
|
|
||||||
super(SpServerError.SP_ATTRIBUTES_TO_MANY_ENTRIES, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param message
|
|
||||||
* of the error
|
|
||||||
*/
|
|
||||||
public ToManyAttributeEntriesException(final String message) {
|
|
||||||
super(message, SpServerError.SP_ATTRIBUTES_TO_MANY_ENTRIES);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
|
||||||
*
|
|
||||||
* All rights reserved. This program and the accompanying materials
|
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
* which accompanies this distribution, and is available at
|
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
*/
|
|
||||||
package org.eclipse.hawkbit.repository.exception;
|
|
||||||
|
|
||||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
|
||||||
import org.eclipse.hawkbit.exception.SpServerError;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown if too many status entries have been inserted.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public final class TooManyStatusEntriesException extends AbstractServerRtException {
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new FileUploadFailedException with
|
|
||||||
* {@link SpServerError#SP_REST_BODY_NOT_READABLE} error.
|
|
||||||
*/
|
|
||||||
public TooManyStatusEntriesException() {
|
|
||||||
super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param cause
|
|
||||||
* for the exception
|
|
||||||
*/
|
|
||||||
public TooManyStatusEntriesException(final Throwable cause) {
|
|
||||||
super(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param message
|
|
||||||
* of the error
|
|
||||||
*/
|
|
||||||
public TooManyStatusEntriesException(final String message) {
|
|
||||||
super(message, SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -43,4 +43,9 @@ public class PropertiesQuotaManagement implements QuotaManagement {
|
|||||||
return securityProperties.getDos().getMaxRolloutGroupsPerRollout();
|
return securityProperties.getDos().getMaxRolloutGroupsPerRollout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxMessagesPerActionStatus() {
|
||||||
|
return securityProperties.getDos().getMaxMessagesPerActionStatus();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.ToManyAttributeEntriesException;
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
|
|
||||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate;
|
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate;
|
||||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||||
@@ -79,7 +78,6 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
@Validated
|
@Validated
|
||||||
public class JpaControllerManagement implements ControllerManagement {
|
public class JpaControllerManagement implements ControllerManagement {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ControllerManagement.class);
|
||||||
private static final Logger LOG_DOS = LoggerFactory.getLogger("server-security.dos");
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
@@ -277,7 +275,8 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// information status entry - check for a potential DOS attack
|
// information status entry - check for a potential DOS attack
|
||||||
checkForToManyStatusEntries(action);
|
checkForTooManyStatusEntries(action);
|
||||||
|
checkForTooManyStatusMessages(actionStatus);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +286,14 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkForTooManyStatusMessages(final JpaActionStatus actionStatus) {
|
||||||
|
if (actionStatus.getMessages().size() > quotaManagement.getMaxMessagesPerActionStatus()) {
|
||||||
|
throw new QuotaExceededException("ActionStatus messages", actionStatus.getMessages().size(),
|
||||||
|
quotaManagement.getMaxStatusEntriesPerAction());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void handleFinishedCancelation(final JpaActionStatus actionStatus, final JpaAction action) {
|
private void handleFinishedCancelation(final JpaActionStatus actionStatus, final JpaAction action) {
|
||||||
// in case of successful cancellation we also report the success at
|
// in case of successful cancellation we also report the success at
|
||||||
// the canceled action itself.
|
// the canceled action itself.
|
||||||
@@ -340,7 +347,8 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// information status entry - check for a potential DOS attack
|
// information status entry - check for a potential DOS attack
|
||||||
checkForToManyStatusEntries(action);
|
checkForTooManyStatusEntries(action);
|
||||||
|
checkForTooManyStatusMessages(actionStatus);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,16 +368,14 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
targetRepository.save(mergedTarget);
|
targetRepository.save(mergedTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkForToManyStatusEntries(final JpaAction action) {
|
private void checkForTooManyStatusEntries(final JpaAction action) {
|
||||||
if (quotaManagement.getMaxStatusEntriesPerAction() > 0) {
|
if (quotaManagement.getMaxStatusEntriesPerAction() > 0) {
|
||||||
|
|
||||||
final Long statusCount = actionStatusRepository.countByAction(action);
|
final Long statusCount = actionStatusRepository.countByAction(action);
|
||||||
|
|
||||||
if (statusCount >= quotaManagement.getMaxStatusEntriesPerAction()) {
|
if (statusCount >= quotaManagement.getMaxStatusEntriesPerAction()) {
|
||||||
LOG_DOS.error(
|
throw new QuotaExceededException(ActionStatus.class, statusCount,
|
||||||
"Potential denial of service (DOS) attack identfied. More status entries in the system than permitted ({})!",
|
|
||||||
quotaManagement.getMaxStatusEntriesPerAction());
|
quotaManagement.getMaxStatusEntriesPerAction());
|
||||||
throw new TooManyStatusEntriesException(String.valueOf(quotaManagement.getMaxStatusEntriesPerAction()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -406,10 +412,8 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
target.getControllerAttributes().putAll(data);
|
target.getControllerAttributes().putAll(data);
|
||||||
|
|
||||||
if (target.getControllerAttributes().size() > quotaManagement.getMaxAttributeEntriesPerTarget()) {
|
if (target.getControllerAttributes().size() > quotaManagement.getMaxAttributeEntriesPerTarget()) {
|
||||||
LOG_DOS.info("Target tries to insert more than the allowed number of entries ({}). DOS attack anticipated!",
|
throw new QuotaExceededException("Controller attribues", target.getControllerAttributes().size(),
|
||||||
quotaManagement.getMaxAttributeEntriesPerTarget());
|
quotaManagement.getMaxAttributeEntriesPerTarget());
|
||||||
throw new ToManyAttributeEntriesException(
|
|
||||||
String.valueOf(quotaManagement.getMaxAttributeEntriesPerTarget()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target.setRequestControllerAttributes(false);
|
target.setRequestControllerAttributes(false);
|
||||||
@@ -488,7 +492,8 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
final JpaActionStatus statusMessage = create.build();
|
final JpaActionStatus statusMessage = create.build();
|
||||||
statusMessage.setAction(action);
|
statusMessage.setAction(action);
|
||||||
|
|
||||||
checkForToManyStatusEntries(action);
|
checkForTooManyStatusEntries(action);
|
||||||
|
checkForTooManyStatusMessages(statusMessage);
|
||||||
|
|
||||||
return actionStatusRepository.save(statusMessage);
|
return actionStatusRepository.save(statusMessage);
|
||||||
}
|
}
|
||||||
@@ -533,11 +538,11 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
|
|
||||||
// For negative and large value of messageCount, limit the number of
|
// For negative and large value of messageCount, limit the number of
|
||||||
// messages.
|
// messages.
|
||||||
int limit = messageCount < 0 || messageCount >= RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
|
final int limit = messageCount < 0 || messageCount >= RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
|
||||||
? RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT : messageCount;
|
? RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT : messageCount;
|
||||||
|
|
||||||
PageRequest pageable = new PageRequest(0, limit, new Sort(Direction.DESC, "occurredAt"));
|
final PageRequest pageable = new PageRequest(0, limit, new Sort(Direction.DESC, "occurredAt"));
|
||||||
Page<String> messages = actionStatusRepository.findMessagesByActionIdAndMessageNotLike(pageable, actionId,
|
final Page<String> messages = actionStatusRepository.findMessagesByActionIdAndMessageNotLike(pageable, actionId,
|
||||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "%");
|
RepositoryConstants.SERVER_MESSAGE_PREFIX + "%");
|
||||||
|
|
||||||
LOG.debug("Retrieved {} message(s) from action history for action {}.", messages.getNumberOfElements(),
|
LOG.debug("Retrieved {} message(s) from action history for action {}.", messages.getNumberOfElements(),
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ public class ResponseExceptionHandler {
|
|||||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH, HttpStatus.BAD_REQUEST);
|
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH, HttpStatus.BAD_REQUEST);
|
||||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_DELETE_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
|
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_DELETE_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_LOAD_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
|
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ARTIFACT_LOAD_FAILED, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ACTION_STATUS_TO_MANY_ENTRIES, HttpStatus.FORBIDDEN);
|
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_QUOTA_EXCEEDED, HttpStatus.FORBIDDEN);
|
||||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ATTRIBUTES_TO_MANY_ENTRIES, HttpStatus.FORBIDDEN);
|
|
||||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ACTION_NOT_CANCELABLE, HttpStatus.METHOD_NOT_ALLOWED);
|
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ACTION_NOT_CANCELABLE, HttpStatus.METHOD_NOT_ALLOWED);
|
||||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ACTION_NOT_FORCE_QUITABLE, HttpStatus.METHOD_NOT_ALLOWED);
|
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ACTION_NOT_FORCE_QUITABLE, HttpStatus.METHOD_NOT_ALLOWED);
|
||||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_CREATION_FAILED_MISSING_MODULE, HttpStatus.BAD_REQUEST);
|
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_CREATION_FAILED_MISSING_MODULE, HttpStatus.BAD_REQUEST);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
package org.eclipse.hawkbit.rest.util;
|
package org.eclipse.hawkbit.rest.util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -206,20 +207,23 @@ public abstract class JsonBuilder {
|
|||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static String deploymentActionFeedback(final String id, final String execution) throws JSONException {
|
public static String deploymentActionFeedback(final String id, final String execution) throws JSONException {
|
||||||
return deploymentActionFeedback(id, execution, "none", RandomStringUtils.randomAscii(1000));
|
return deploymentActionFeedback(id, execution, "none", Arrays.asList(RandomStringUtils.randomAscii(1000)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String deploymentActionFeedback(final String id, final String execution, final String message)
|
public static String deploymentActionFeedback(final String id, final String execution, final String message)
|
||||||
throws JSONException {
|
throws JSONException {
|
||||||
return deploymentActionFeedback(id, execution, "none", message);
|
return deploymentActionFeedback(id, execution, "none", Arrays.asList(message));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String deploymentActionFeedback(final String id, final String execution, final String finished,
|
public static String deploymentActionFeedback(final String id, final String execution, final String finished,
|
||||||
final String message) throws JSONException {
|
final String message) throws JSONException {
|
||||||
final List<String> messages = new ArrayList<>();
|
return deploymentActionFeedback(id, execution, finished, Arrays.asList(message));
|
||||||
messages.add(message);
|
}
|
||||||
|
|
||||||
|
public static String deploymentActionFeedback(final String id, final String execution, final String finished,
|
||||||
|
final Collection<String> messages) throws JSONException {
|
||||||
|
|
||||||
return new JSONObject().put("id", id).put("time", "20140511T121314")
|
return new JSONObject().put("id", id).put("time", "20140511T121314")
|
||||||
.put("status",
|
.put("status",
|
||||||
|
|||||||
@@ -109,6 +109,11 @@ public class HawkbitSecurityProperties {
|
|||||||
*/
|
*/
|
||||||
private int maxRolloutGroupsPerRollout = 500;
|
private int maxRolloutGroupsPerRollout = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of messages per ActionStatus
|
||||||
|
*/
|
||||||
|
private int maxMessagesPerActionStatus = 50;
|
||||||
|
|
||||||
private final Filter filter = new Filter();
|
private final Filter filter = new Filter();
|
||||||
private final Filter uiFilter = new Filter();
|
private final Filter uiFilter = new Filter();
|
||||||
|
|
||||||
@@ -128,6 +133,14 @@ public class HawkbitSecurityProperties {
|
|||||||
this.maxStatusEntriesPerAction = maxStatusEntriesPerAction;
|
this.maxStatusEntriesPerAction = maxStatusEntriesPerAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getMaxMessagesPerActionStatus() {
|
||||||
|
return maxMessagesPerActionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxMessagesPerActionStatus(final int maxMessagesPerActionStatus) {
|
||||||
|
this.maxMessagesPerActionStatus = maxMessagesPerActionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
public int getMaxAttributeEntriesPerTarget() {
|
public int getMaxAttributeEntriesPerTarget() {
|
||||||
return maxAttributeEntriesPerTarget;
|
return maxAttributeEntriesPerTarget;
|
||||||
}
|
}
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@@ -155,7 +155,7 @@
|
|||||||
|
|
||||||
<!-- Misc libraries versions - START -->
|
<!-- Misc libraries versions - START -->
|
||||||
<validation-api.version>1.1.0.Final</validation-api.version>
|
<validation-api.version>1.1.0.Final</validation-api.version>
|
||||||
<allure.version>1.4.22</allure.version>
|
<allure.version>1.5.4</allure.version>
|
||||||
<eclipselink.version>2.6.4</eclipselink.version>
|
<eclipselink.version>2.6.4</eclipselink.version>
|
||||||
<org.powermock.version>1.6.5</org.powermock.version>
|
<org.powermock.version>1.6.5</org.powermock.version>
|
||||||
<gwtmockito.version>1.1.6</gwtmockito.version>
|
<gwtmockito.version>1.1.6</gwtmockito.version>
|
||||||
|
|||||||
Reference in New Issue
Block a user