From ea95f487202a7e45c4f587e9de3ccb28d12708b4 Mon Sep 17 00:00:00 2001 From: MDeLuise <66636702+MDeLuise@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:38:57 +0100 Subject: [PATCH] fix: respond with 403 when max allowed limit for entities is exceeded Ensure the backend returns HTTP 403 (Forbidden) instead of HTTP 500 (Internal Server Error) when any entity exceeds the maximum allowed limit as defined by service settings. --- ...axNumberOfItemsReachedExceptionMapper.java | 47 ++++++++++++++ .../MaxNumberOfItemsReachedExceptionInfo.java | 63 +++++++++++++++++++ .../eclipse/kapua/qa/common/BasicSteps.java | 35 +++++++---- .../AccountDeviceRegistryService.feature | 2 + .../account/AccountGroupService.feature | 2 + .../account/AccountJobService.feature | 2 + .../account/AccountRoleService.feature | 2 + .../account/AccountServiceCreation.feature | 2 + .../account/AccountTagService.feature | 2 + .../account/AccountUserService.feature | 2 + 10 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 commons-rest/errors/src/main/java/org/eclipse/kapua/commons/rest/errors/KapuaMaxNumberOfItemsReachedExceptionMapper.java create mode 100644 commons-rest/model/src/main/java/org/eclipse/kapua/commons/rest/model/errors/MaxNumberOfItemsReachedExceptionInfo.java diff --git a/commons-rest/errors/src/main/java/org/eclipse/kapua/commons/rest/errors/KapuaMaxNumberOfItemsReachedExceptionMapper.java b/commons-rest/errors/src/main/java/org/eclipse/kapua/commons/rest/errors/KapuaMaxNumberOfItemsReachedExceptionMapper.java new file mode 100644 index 00000000000..08cbd994ede --- /dev/null +++ b/commons-rest/errors/src/main/java/org/eclipse/kapua/commons/rest/errors/KapuaMaxNumberOfItemsReachedExceptionMapper.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2024 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech - initial API and implementation + *******************************************************************************/ +package org.eclipse.kapua.commons.rest.errors; + +import javax.inject.Inject; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.eclipse.kapua.KapuaMaxNumberOfItemsReachedException; +import org.eclipse.kapua.commons.rest.model.errors.MaxNumberOfItemsReachedExceptionInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Provider +public class KapuaMaxNumberOfItemsReachedExceptionMapper implements ExceptionMapper { + + private static final Logger LOG = LoggerFactory.getLogger(KapuaMaxNumberOfItemsReachedExceptionMapper.class); + private final boolean showStackTrace; + + + @Inject + public KapuaMaxNumberOfItemsReachedExceptionMapper(ExceptionConfigurationProvider exceptionConfigurationProvider) { + this.showStackTrace = exceptionConfigurationProvider.showStackTrace(); + } + + + @Override + public Response toResponse(KapuaMaxNumberOfItemsReachedException kapuaMaxNumberOfItemsReachedException) { + LOG.error(kapuaMaxNumberOfItemsReachedException.getMessage(), kapuaMaxNumberOfItemsReachedException); + return Response + .status(Status.FORBIDDEN) + .entity(new MaxNumberOfItemsReachedExceptionInfo(Status.FORBIDDEN.getStatusCode(), kapuaMaxNumberOfItemsReachedException, showStackTrace)) + .build(); + } +} diff --git a/commons-rest/model/src/main/java/org/eclipse/kapua/commons/rest/model/errors/MaxNumberOfItemsReachedExceptionInfo.java b/commons-rest/model/src/main/java/org/eclipse/kapua/commons/rest/model/errors/MaxNumberOfItemsReachedExceptionInfo.java new file mode 100644 index 00000000000..0768e12fc6b --- /dev/null +++ b/commons-rest/model/src/main/java/org/eclipse/kapua/commons/rest/model/errors/MaxNumberOfItemsReachedExceptionInfo.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2024 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech - initial API and implementation + *******************************************************************************/ +package org.eclipse.kapua.commons.rest.model.errors; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.eclipse.kapua.KapuaMaxNumberOfItemsReachedException; + +@XmlRootElement(name = "maxNumberOfItemsReachedExceptionInfo") +@XmlAccessorType(XmlAccessType.FIELD) +public class MaxNumberOfItemsReachedExceptionInfo extends ExceptionInfo { + + @XmlElement(name = "entityType") + private String entityType; + + + /** + * Constructor. + * + * @since 2.0.0 + */ + protected MaxNumberOfItemsReachedExceptionInfo() { + super(); + } + + + /** + * Constructor. + * + * @param httpStatusCode The http status code of the response containing this info + * @param kapuaMaxNumberOfItemsReachedException The root exception. + * @since 2.0.0 + */ + public MaxNumberOfItemsReachedExceptionInfo(int httpStatusCode, KapuaMaxNumberOfItemsReachedException kapuaMaxNumberOfItemsReachedException, boolean showStackTrace) { + super(httpStatusCode, kapuaMaxNumberOfItemsReachedException, showStackTrace); + this.entityType = kapuaMaxNumberOfItemsReachedException.getEntityType(); + } + + + /** + * Gets the {@link KapuaMaxNumberOfItemsReachedException#getEntityType()}. + * + * @return The {@link KapuaMaxNumberOfItemsReachedException#getEntityType()}. + * @since 2.0.0 + */ + public String getEntityType() { + return entityType; + } + +} diff --git a/qa/common/src/main/java/org/eclipse/kapua/qa/common/BasicSteps.java b/qa/common/src/main/java/org/eclipse/kapua/qa/common/BasicSteps.java index 1cebf7f2361..2b8399c9b13 100644 --- a/qa/common/src/main/java/org/eclipse/kapua/qa/common/BasicSteps.java +++ b/qa/common/src/main/java/org/eclipse/kapua/qa/common/BasicSteps.java @@ -18,17 +18,6 @@ import java.util.Map; import javax.inject.Inject; -import com.google.common.base.Strings; -import com.google.inject.Singleton; -import io.cucumber.java.After; -import io.cucumber.java.Before; -import io.cucumber.java.DataTableType; -import io.cucumber.java.ParameterType; -import io.cucumber.java.Scenario; -import io.cucumber.java.en.And; -import io.cucumber.java.en.Given; -import io.cucumber.java.en.Then; -import io.cucumber.java.en.When; import org.apache.shiro.SecurityUtils; import org.eclipse.kapua.commons.crypto.setting.CryptoSettingKeys; import org.eclipse.kapua.commons.security.KapuaSecurityUtils; @@ -61,6 +50,17 @@ import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Strings; +import com.google.inject.Singleton; +import io.cucumber.java.After; +import io.cucumber.java.Before; +import io.cucumber.java.DataTableType; +import io.cucumber.java.ParameterType; +import io.cucumber.java.Scenario; +import io.cucumber.java.en.And; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; @Singleton public class BasicSteps extends TestBase { @@ -88,6 +88,7 @@ public class BasicSteps extends TestBase { private static final String LAST_ACCOUNT_ID = "LastAccountId"; private static final String LAST_USER_ID = "LastUserId"; private static final String EXCEPTION_NAME = "ExceptionName"; + private static final String STATUS_CODE = "StatusCode"; private static final String EXCEPTION_CAUGHT = "ExceptionCaught"; private static final String ASSERT_ERROR_NAME = "AssertErrorName"; private static final String ASSERT_ERROR_CAUGHT = "AssertErrorCaught"; @@ -532,6 +533,12 @@ public void noExceptionCaught() { Assert.assertFalse("An unexpected exception was raised!", exCaught); } + @Then("Response status code match") + public void statusCodeMatch() { + final int statusCode = stepData.contains(STATUS_CODE) ? (int) stepData.get(STATUS_CODE) : -1; + Assert.assertEquals(statusCode, stepData.get("StatusCode")); + } + @Then("I count {int}") public void checkCountResult(int num) { Assert.assertEquals(num, stepData.getCount()); @@ -588,6 +595,12 @@ public void iExpectTheException(String name) { stepData.put(EXCEPTION_NAME, name); } + @And("I expect the response status code {int}") + public void iExpectTheStatusCode(int statusCode) { + stepData.put("StatusCodeExpected", true); + stepData.put(STATUS_CODE, statusCode); + } + @Then("An assertion error was thrown") public void anAssertionErrorWasThrown() { String assertErrorName = stepData.contains(ASSERT_ERROR_NAME) ? (String) stepData.get(ASSERT_ERROR_NAME) : "Unknown"; diff --git a/qa/integration/src/test/resources/features/account/AccountDeviceRegistryService.feature b/qa/integration/src/test/resources/features/account/AccountDeviceRegistryService.feature index ebc2a4e85fc..1e54d182e16 100644 --- a/qa/integration/src/test/resources/features/account/AccountDeviceRegistryService.feature +++ b/qa/integration/src/test/resources/features/account/AccountDeviceRegistryService.feature @@ -56,8 +56,10 @@ Feature: Account Device Registry Service Integration Tests And I create a device with name "Device3" Then No exception was thrown Given I expect the exception "KapuaMaxNumberOfItemsReachedException" with the text "*" + And I expect the response status code 403 When I create a device with name "Device4" Then An exception was thrown + And Response status code match And I logout Scenario: Creating Devices Under Account That Does Not Allow Devices diff --git a/qa/integration/src/test/resources/features/account/AccountGroupService.feature b/qa/integration/src/test/resources/features/account/AccountGroupService.feature index 90957fe3d7d..f80fc33a698 100644 --- a/qa/integration/src/test/resources/features/account/AccountGroupService.feature +++ b/qa/integration/src/test/resources/features/account/AccountGroupService.feature @@ -54,8 +54,10 @@ Feature: Account Group Service Integration Tests And I create a group with name "Group2" And I create a group with name "Group3" Given I expect the exception "KapuaMaxNumberOfItemsReachedException" with the text "*" + And I expect the response status code 403 And I create a group with name "Group4" Then An exception was thrown + And Response status code match And I logout Scenario: Creating Groups Under Account That Does Not Allow Groups diff --git a/qa/integration/src/test/resources/features/account/AccountJobService.feature b/qa/integration/src/test/resources/features/account/AccountJobService.feature index 407f35b3aab..364c73376c0 100644 --- a/qa/integration/src/test/resources/features/account/AccountJobService.feature +++ b/qa/integration/src/test/resources/features/account/AccountJobService.feature @@ -57,8 +57,10 @@ Feature: Account Job Service Integration Tests And I create a job with the name "job3" Then No exception was thrown Given I expect the exception "KapuaMaxNumberOfItemsReachedException" with the text "*" + And I expect the response status code 403 When I create a job with the name "job4" Then An exception was thrown + And Response status code match And I logout Scenario: Creating Jobs Under Account That Does Not Allow Jobs diff --git a/qa/integration/src/test/resources/features/account/AccountRoleService.feature b/qa/integration/src/test/resources/features/account/AccountRoleService.feature index 45b9aaf4b36..6a6ee0032c2 100644 --- a/qa/integration/src/test/resources/features/account/AccountRoleService.feature +++ b/qa/integration/src/test/resources/features/account/AccountRoleService.feature @@ -55,8 +55,10 @@ Feature: Account Role Service Integration Tests And I create role "role2" in account "acc1" And I create role "role3" in account "acc1" Given I expect the exception "KapuaMaxNumberOfItemsReachedException" with the text "*" + And I expect the response status code 403 When I create role "role4" in account "acc1" Then An exception was thrown + And Response status code match And I logout Scenario: Creating Roles Under Account That Does Not Allow Roles diff --git a/qa/integration/src/test/resources/features/account/AccountServiceCreation.feature b/qa/integration/src/test/resources/features/account/AccountServiceCreation.feature index c4cc727ab66..f695a3b211c 100644 --- a/qa/integration/src/test/resources/features/account/AccountServiceCreation.feature +++ b/qa/integration/src/test/resources/features/account/AccountServiceCreation.feature @@ -217,8 +217,10 @@ Feature: Account Service Tests | integer | maxNumberChildEntities | 0 | Then I select account "acc1" Given I expect the exception "KapuaMaxNumberOfItemsReachedException" with the text "*" + And I expect the response status code 403 And I create an account with name "acc11", organization name "acc11" and email address "acc11@org.com" Then An exception was thrown + And Response status code match Then I logout Scenario: Creating Sub-accounts When InfiniteChildAccounts Is Set To True And maxNumberChildAccounts Is Set diff --git a/qa/integration/src/test/resources/features/account/AccountTagService.feature b/qa/integration/src/test/resources/features/account/AccountTagService.feature index 4c3adeef6b6..dd7317cdf06 100644 --- a/qa/integration/src/test/resources/features/account/AccountTagService.feature +++ b/qa/integration/src/test/resources/features/account/AccountTagService.feature @@ -55,8 +55,10 @@ Feature: Account Tag Service Integration Tests And I create tag with name "tag2" without description And I create tag with name "tag3" without description Given I expect the exception "KapuaMaxNumberOfItemsReachedException" with the text "*" + And I expect the response status code 403 When I create tag with name "tag4" without description Then An exception was thrown + And Response status code match And I logout Scenario: Creating Tags Under Account That Does Not Allow Tags diff --git a/qa/integration/src/test/resources/features/account/AccountUserService.feature b/qa/integration/src/test/resources/features/account/AccountUserService.feature index 73a4313e9dc..f211281f0b2 100644 --- a/qa/integration/src/test/resources/features/account/AccountUserService.feature +++ b/qa/integration/src/test/resources/features/account/AccountUserService.feature @@ -55,8 +55,10 @@ Feature: Account User Service Integration Tests And I create user with name "user2" And I create user with name "user3" Given I expect the exception "KapuaMaxNumberOfItemsReachedException" with the text "*" + And I expect the response status code 403 When I create user with name "user4" Then An exception was thrown + And Response status code match And I logout Scenario: Creating Users Under Account That Does Not Allow Users