From f6fecb114b504efef21d91894f675d18183bc882 Mon Sep 17 00:00:00 2001 From: Savindu Dimal Date: Thu, 12 Oct 2023 16:19:11 +0530 Subject: [PATCH 01/39] Add integration tests for validate API product swagger definition --- .../clients/publisher/api/openapi.yaml | 5 +- .../clients/publisher/docs/ApiProductsApi.md | 7 +- .../publisher/api/v1/ApiProductsApi.java | 18 ++- .../src/main/resources/publisher-api.yaml | 6 +- .../test/impl/RestAPIPublisherImpl.java | 7 + .../APIProductCreationTestCase.java | 75 ++++++++++ .../oas/v3/api-product/test-api-1-oas.yaml | 139 ++++++++++++++++++ 7 files changed, 245 insertions(+), 12 deletions(-) create mode 100644 modules/integration/tests-integration/tests-backend/src/test/resources/oas/v3/api-product/test-api-1-oas.yaml diff --git a/modules/integration/tests-common/clients/publisher/api/openapi.yaml b/modules/integration/tests-common/clients/publisher/api/openapi.yaml index b4ce30723f..9ca4fd290c 100644 --- a/modules/integration/tests-common/clients/publisher/api/openapi.yaml +++ b/modules/integration/tests-common/clients/publisher/api/openapi.yaml @@ -10187,7 +10187,10 @@ paths: style: simple responses: "200": - content: {} + content: + application/json: + schema: + type: string description: | OK. Requested swagger document of the API is returned diff --git a/modules/integration/tests-common/clients/publisher/docs/ApiProductsApi.md b/modules/integration/tests-common/clients/publisher/docs/ApiProductsApi.md index 5ad8c3153c..869926f3c5 100644 --- a/modules/integration/tests-common/clients/publisher/docs/ApiProductsApi.md +++ b/modules/integration/tests-common/clients/publisher/docs/ApiProductsApi.md @@ -231,7 +231,7 @@ Name | Type | Description | Notes # **getAPIProductSwagger** -> getAPIProductSwagger(apiProductId, accept, ifNoneMatch) +> String getAPIProductSwagger(apiProductId, accept, ifNoneMatch) Get Swagger Definition @@ -261,7 +261,8 @@ public class Example { String accept = "\"application/json\""; // String | Media types acceptable for the response. Default is application/json. String ifNoneMatch = "ifNoneMatch_example"; // String | Validator for conditional requests; based on the ETag of the formerly retrieved variant of the resource. try { - apiInstance.getAPIProductSwagger(apiProductId, accept, ifNoneMatch); + String result = apiInstance.getAPIProductSwagger(apiProductId, accept, ifNoneMatch); + System.out.println(result); } catch (ApiException e) { System.err.println("Exception when calling ApiProductsApi#getAPIProductSwagger"); System.err.println("Status code: " + e.getCode()); @@ -283,7 +284,7 @@ Name | Type | Description | Notes ### Return type -null (empty response body) +**String** ### Authorization diff --git a/modules/integration/tests-common/clients/publisher/src/gen/java/org/wso2/am/integration/clients/publisher/api/v1/ApiProductsApi.java b/modules/integration/tests-common/clients/publisher/src/gen/java/org/wso2/am/integration/clients/publisher/api/v1/ApiProductsApi.java index 58c6592172..ebf588dbb9 100644 --- a/modules/integration/tests-common/clients/publisher/src/gen/java/org/wso2/am/integration/clients/publisher/api/v1/ApiProductsApi.java +++ b/modules/integration/tests-common/clients/publisher/src/gen/java/org/wso2/am/integration/clients/publisher/api/v1/ApiProductsApi.java @@ -518,6 +518,7 @@ private okhttp3.Call getAPIProductSwaggerValidateBeforeCall(String apiProductId, * @param apiProductId **API Product ID** consisting of the **UUID** of the API Product. Using the **UUID** in the API call is recommended. (required) * @param accept Media types acceptable for the response. Default is application/json. (optional, default to "application/json") * @param ifNoneMatch Validator for conditional requests; based on the ETag of the formerly retrieved variant of the resource. (optional) + * @return String * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body * @http.response.details @@ -528,8 +529,9 @@ private okhttp3.Call getAPIProductSwaggerValidateBeforeCall(String apiProductId,
406 Not Acceptable. The requested media type is not supported. -
*/ - public void getAPIProductSwagger(String apiProductId, String accept, String ifNoneMatch) throws ApiException { - getAPIProductSwaggerWithHttpInfo(apiProductId, accept, ifNoneMatch); + public String getAPIProductSwagger(String apiProductId, String accept, String ifNoneMatch) throws ApiException { + ApiResponse localVarResp = getAPIProductSwaggerWithHttpInfo(apiProductId, accept, ifNoneMatch); + return localVarResp.getData(); } /** @@ -538,7 +540,7 @@ public void getAPIProductSwagger(String apiProductId, String accept, String ifNo * @param apiProductId **API Product ID** consisting of the **UUID** of the API Product. Using the **UUID** in the API call is recommended. (required) * @param accept Media types acceptable for the response. Default is application/json. (optional, default to "application/json") * @param ifNoneMatch Validator for conditional requests; based on the ETag of the formerly retrieved variant of the resource. (optional) - * @return ApiResponse<Void> + * @return ApiResponse<String> * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body * @http.response.details @@ -549,9 +551,10 @@ public void getAPIProductSwagger(String apiProductId, String accept, String ifNo
406 Not Acceptable. The requested media type is not supported. -
*/ - public ApiResponse getAPIProductSwaggerWithHttpInfo(String apiProductId, String accept, String ifNoneMatch) throws ApiException { + public ApiResponse getAPIProductSwaggerWithHttpInfo(String apiProductId, String accept, String ifNoneMatch) throws ApiException { okhttp3.Call localVarCall = getAPIProductSwaggerValidateBeforeCall(apiProductId, accept, ifNoneMatch, null); - return localVarApiClient.execute(localVarCall); + Type localVarReturnType = new TypeToken(){}.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); } /** @@ -572,10 +575,11 @@ public ApiResponse getAPIProductSwaggerWithHttpInfo(String apiProductId, S 406 Not Acceptable. The requested media type is not supported. - */ - public okhttp3.Call getAPIProductSwaggerAsync(String apiProductId, String accept, String ifNoneMatch, final ApiCallback _callback) throws ApiException { + public okhttp3.Call getAPIProductSwaggerAsync(String apiProductId, String accept, String ifNoneMatch, final ApiCallback _callback) throws ApiException { okhttp3.Call localVarCall = getAPIProductSwaggerValidateBeforeCall(apiProductId, accept, ifNoneMatch, _callback); - localVarApiClient.executeAsync(localVarCall, _callback); + Type localVarReturnType = new TypeToken(){}.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); return localVarCall; } /** diff --git a/modules/integration/tests-common/clients/publisher/src/main/resources/publisher-api.yaml b/modules/integration/tests-common/clients/publisher/src/main/resources/publisher-api.yaml index de67456fcb..4782209fc1 100644 --- a/modules/integration/tests-common/clients/publisher/src/main/resources/publisher-api.yaml +++ b/modules/integration/tests-common/clients/publisher/src/main/resources/publisher-api.yaml @@ -5693,7 +5693,11 @@ paths: The content type of the body. schema: type: string - content: {} + content: + application/json: + schema: + type: string + example: "" 304: description: | Not Modified. diff --git a/modules/integration/tests-common/integration-test-utils/src/main/java/org/wso2/am/integration/test/impl/RestAPIPublisherImpl.java b/modules/integration/tests-common/integration-test-utils/src/main/java/org/wso2/am/integration/test/impl/RestAPIPublisherImpl.java index f1c676f2bc..94a8aaa301 100644 --- a/modules/integration/tests-common/integration-test-utils/src/main/java/org/wso2/am/integration/test/impl/RestAPIPublisherImpl.java +++ b/modules/integration/tests-common/integration-test-utils/src/main/java/org/wso2/am/integration/test/impl/RestAPIPublisherImpl.java @@ -1124,6 +1124,13 @@ public String getSwaggerByID(String apiId) throws ApiException { return response.getData(); } + public String getAPIProductSwaggerByID(String apiProductId) throws ApiException { + + ApiResponse response = apiProductsApi.getAPIProductSwaggerWithHttpInfo(apiProductId, null, null); + Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + return response.getData(); + } + public String updateSwagger(String apiId, String definition) throws ApiException { ApiResponse apiResponse = apIsApi.updateAPISwaggerWithHttpInfo(apiId, null, definition, null, null); diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/apiproduct/APIProductCreationTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/apiproduct/APIProductCreationTestCase.java index bf294f02b5..7f6cb9e9cb 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/apiproduct/APIProductCreationTestCase.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/apiproduct/APIProductCreationTestCase.java @@ -35,6 +35,7 @@ import org.wso2.am.integration.clients.publisher.api.v1.dto.APIOperationPoliciesDTO; import org.wso2.am.integration.clients.publisher.api.v1.dto.APIProductDTO; import org.wso2.am.integration.clients.publisher.api.v1.dto.LifecycleStateDTO; +import org.wso2.am.integration.clients.publisher.api.v1.dto.OpenAPIDefinitionValidationResponseDTO; import org.wso2.am.integration.clients.publisher.api.v1.dto.OperationPolicyDTO; import org.wso2.am.integration.clients.publisher.api.v1.dto.WorkflowResponseDTO; import org.wso2.am.integration.clients.publisher.api.v1.dto.APIOperationsDTO; @@ -49,10 +50,13 @@ import org.wso2.am.integration.test.utils.bean.APILifeCycleState; import org.wso2.am.integration.tests.api.lifecycle.APIManagerLifecycleBaseTest; import org.wso2.carbon.automation.engine.context.TestUserMode; +import org.wso2.carbon.automation.test.utils.common.TestConfigurationProvider; import org.wso2.carbon.automation.test.utils.http.client.HttpResponse; import org.wso2.carbon.integration.common.admin.client.UserManagementClient; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -65,6 +69,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class APIProductCreationTestCase extends APIManagerLifecycleBaseTest { @@ -76,6 +81,7 @@ public class APIProductCreationTestCase extends APIManagerLifecycleBaseTest { private static final String SCOPE = "restricted_scope"; private ApiTestHelper apiTestHelper; private ApiProductTestHelper apiProductTestHelper; + private String resourcePath; @Factory(dataProvider = "userModeDataProvider") public APIProductCreationTestCase(TestUserMode userMode) { @@ -96,6 +102,8 @@ public static Object[][] userModeDataProvider() { public void initialize() throws Exception { super.init(userMode); + resourcePath = TestConfigurationProvider.getResourceLocation() + File.separator + "oas" + File.separator + "v3" + + File.separator + "api-product" + File.separator; userManagementClient = new UserManagementClient(keyManagerContext.getContextUrls().getBackEndUrl(), createSession(keyManagerContext)); apiTestHelper = new ApiTestHelper(restAPIPublisher, restAPIStore, getAMResourceLocation(), @@ -605,6 +613,73 @@ public void testCreateAndDeployApiProductWithMutualSSLEnabled() throws Exception Assert.assertNotNull(revisionUUID); } + @Test(groups = { "wso2.am" }, description = "API product swagger definition reference verification") + public void testAPIProductSwaggerDefinition() throws Exception { + + // Create a REST API using swagger definition + List apisToBeUsed = new ArrayList<>(); + String swaggerPath = resourcePath + "test-api-1-oas.yaml"; + File definition = new File(swaggerPath); + org.json.JSONObject endpoints = new org.json.JSONObject(); + endpoints.put("url", "https://test.com"); + + org.json.JSONObject endpointConfig = new org.json.JSONObject(); + endpointConfig.put("endpoint_type", "http"); + endpointConfig.put("production_endpoints", endpoints); + endpointConfig.put("sandbox_endpoints", endpoints); + + List tierList = new ArrayList<>(); + tierList.add(APIMIntegrationConstants.API_TIER.SILVER); + tierList.add(APIMIntegrationConstants.API_TIER.GOLD); + + org.json.JSONObject apiProperties = new org.json.JSONObject(); + apiProperties.put("name", "testAPI1"); + apiProperties.put("context", "/testapi1"); + apiProperties.put("version", "1.0.0"); + apiProperties.put("provider", user.getUserName()); + apiProperties.put("endpointConfig", endpointConfig); + apiProperties.put("policies", tierList); + + APIDTO apiOne = restAPIPublisher.importOASDefinition(definition, apiProperties.toString()); + APIDTO apiTwo = apiTestHelper.createApiTwo(getBackendEndServiceEndPointHttp("wildcard/resources")); + apisToBeUsed.add(apiOne); + apisToBeUsed.add(apiTwo); + + // Create API Product and verify in publisher + final String provider = user.getUserName(); + final String name = UUID.randomUUID().toString(); + final String context = "/" + UUID.randomUUID().toString(); + + List policies = Arrays.asList(TIER_UNLIMITED, TIER_GOLD); + + APIProductDTO apiProductDTO = apiProductTestHelper.createAPIProductInPublisher( + provider, name, context, apisToBeUsed, policies); + createAPIProductRevisionAndDeployUsingRest(apiProductDTO.getId(), restAPIPublisher); + waitForAPIDeployment(); + apiProductTestHelper.verfiyApiProductInPublisher(apiProductDTO); + apiProductDTO = publishAPIProduct(apiProductDTO.getId()); + String apiProductID = apiProductDTO.getId(); + + // Get api product definition and validate + String apiProductDefinition = restAPIPublisher.getAPIProductSwaggerByID(apiProductID); + validateDefinition(apiProductDefinition); + } + + private void validateDefinition(String oasDefinition) throws Exception { + File file = geTempFileWithContent(oasDefinition); + OpenAPIDefinitionValidationResponseDTO responseDTO = restAPIPublisher.validateOASDefinition(file); + assertTrue(responseDTO.isIsValid()); + } + + private File geTempFileWithContent(String swagger) throws Exception { + File temp = File.createTempFile("swagger", ".json"); + temp.deleteOnExit(); + BufferedWriter out = new BufferedWriter(new FileWriter(temp)); + out.write(swagger); + out.close(); + return temp; + } + @AfterClass(alwaysRun = true) public void cleanUpArtifacts() throws Exception { diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/oas/v3/api-product/test-api-1-oas.yaml b/modules/integration/tests-integration/tests-backend/src/test/resources/oas/v3/api-product/test-api-1-oas.yaml new file mode 100644 index 0000000000..0ad7037e8e --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/resources/oas/v3/api-product/test-api-1-oas.yaml @@ -0,0 +1,139 @@ +openapi: 3.0.1 +info: + title: testAPI1 + version: 1.0.0 +servers: + - url: / +security: + - default: [] +paths: + "/users/{userID}/products/": + get: + parameters: + - name: userID + in: path + required: true + style: simple + explode: false + schema: + type: string + format: string + responses: + "200": + description: ok + security: + - default: [] + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + x-wso2-application-security: + security-types: + - oauth2 + optional: false + "/users/{userID}/products/{productID}": + get: + parameters: + - $ref: "#/components/parameters/userIDPathParam" + responses: + "200": + description: ok + security: + - default: [] + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + x-wso2-application-security: + security-types: + - oauth2 + optional: false + post: + parameters: + - $ref: "#/components/parameters/userIDPathParam" + responses: + "200": + description: ok + headers: + location: + $ref: "#/components/headers/Location" + security: + - default: [] + x-auth-type: Application & Application User + x-throttling-tier: Unlimited + x-wso2-application-security: + security-types: + - oauth2 + optional: false + parameters: + - $ref: "#/components/parameters/ProductIDPathParam" +components: + parameters: + ProductIDPathParam: + name: productID + in: path + required: true + style: simple + explode: false + schema: + type: string + format: string + userIDPathParam: + name: userID + in: path + required: true + style: simple + explode: false + schema: + type: string + format: string + headers: + Location: + description: Location of the new created resource + style: simple + explode: false + schema: + type: string + format: uri + securitySchemes: + default: + type: oauth2 + flows: + implicit: + authorizationUrl: "https://test.com" + scopes: {} +x-wso2-auth-header: Authorization +x-wso2-cors: + corsConfigurationEnabled: false + accessControlAllowOrigins: + - "*" + accessControlAllowCredentials: false + accessControlAllowHeaders: + - authorization + - Access-Control-Allow-Origin + - Content-Type + - SOAPAction + - apikey + - Internal-Key + accessControlAllowMethods: + - GET + - PUT + - POST + - DELETE + - PATCH + - OPTIONS +x-wso2-production-endpoints: + urls: + - "http://api.yourdomain.com/" + type: http +x-wso2-sandbox-endpoints: + urls: + - "http://api.yourdomain.com/" + type: http +x-wso2-basePath: /testapi1/1.0.0 +x-wso2-transports: + - http + - https +x-wso2-application-security: + security-types: + - oauth2 + optional: false +x-wso2-response-cache: + enabled: false + cacheTimeoutInSeconds: 300 From 2fe92062ba9eb68d2601e575e25651bb6477be23 Mon Sep 17 00:00:00 2001 From: GihanAyesh Date: Wed, 29 Nov 2023 12:57:14 +0530 Subject: [PATCH 02/39] JWT Decoding test case --- .../jwt/jwtdecoding/JWTDecodingTestCase.java | 199 ++++++++++++++++++ .../urlSafeTokenTest/deployment.toml | 14 ++ .../src/test/resources/testng.xml | 1 + 3 files changed, 214 insertions(+) create mode 100644 modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/jwt/jwtdecoding/JWTDecodingTestCase.java diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/jwt/jwtdecoding/JWTDecodingTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/jwt/jwtdecoding/JWTDecodingTestCase.java new file mode 100644 index 0000000000..b6a6b4de74 --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/jwt/jwtdecoding/JWTDecodingTestCase.java @@ -0,0 +1,199 @@ +/* + *Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + *WSO2 LLC. licenses this file to you under the Apache License, + *Version 2.0 (the "License"); you may not use this file except + *in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, + *software distributed under the License is distributed on an + *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + *KIND, either express or implied. See the License for the + *specific language governing permissions and limitations + *under the License. + */ + +package org.wso2.am.integration.tests.jwt.jwtdecoding; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClientBuilder; +import org.json.JSONException; +import org.json.JSONObject; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; +import org.wso2.am.integration.clients.store.api.ApiResponse; +import org.wso2.am.integration.clients.store.api.v1.dto.APIKeyDTO; +import org.wso2.am.integration.clients.store.api.v1.dto.ApplicationDTO; +import org.wso2.am.integration.clients.store.api.v1.dto.ApplicationKeyDTO; +import org.wso2.am.integration.clients.store.api.v1.dto.ApplicationKeyGenerateRequestDTO; +import org.wso2.am.integration.test.utils.APIManagerIntegrationTestException; +import org.wso2.am.integration.test.utils.base.APIMIntegrationConstants; +import org.wso2.am.integration.test.utils.bean.APIRequest; +import org.wso2.am.integration.tests.api.lifecycle.APIManagerLifecycleBaseTest; +import org.wso2.carbon.automation.engine.context.TestUserMode; +import org.wso2.carbon.um.ws.api.stub.ClaimValue; +import org.wso2.carbon.um.ws.api.stub.RemoteUserStoreManagerServiceUserStoreExceptionException; +import org.wso2.carbon.user.core.UserStoreException; + +import javax.ws.rs.core.Response; +import java.net.URL; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Test for the API invocation failing when enabling the backend JWT and with the username pattern [string].[string] + * It performs the following actions: + * 1. Calls an API using an API key and checks the response status code. + * 2. Repeats the API call and check status code as this was failing before the fix. + * 3. Calls the same API using a Bearer token and checks the response status code. + * 4. Repeats the Bearer token API call. + */ +public class JWTDecodingTestCase extends APIManagerLifecycleBaseTest { + + private static final Log log = LogFactory.getLog(JWTDecodingTestCase.class); + private final String decodingApiContext = "jwtdecodingTest"; + private final String apiVersion = "1.0.0"; + String enduserName = "admin.abc"; + String enduserPassword = "password@123"; + URL tokenEndpointURL; + private String decodingApplicationId; + private String decodingApiId; + + @Factory(dataProvider = "userModeDataProvider") public JWTDecodingTestCase(TestUserMode userMode) { + this.userMode = userMode; + } + + @DataProvider public static Object[][] userModeDataProvider() { + return new Object[][] { new Object[] { TestUserMode.SUPER_TENANT_ADMIN }, + new Object[] { TestUserMode.TENANT_ADMIN } }; + } + + @BeforeClass(alwaysRun = true) public void setEnvironment() throws Exception { + super.init(userMode); + tokenEndpointURL = new URL(keyManagerHTTPSURL + "oauth2/token"); + String providerName = user.getUserName(); + String endpointURL = getSuperTenantAPIInvocationURLHttp("jwt_backend", "1.0"); + Map attributes = new HashMap<>(); + attributes.put("Sandbox access required", "Yes"); + attributes.put("Production access required", "Yes"); + + //create JWT Base App with custom attributes + String decodingApplicationName = "JWTAppFOrJWTDecodingTest"; + org.wso2.carbon.automation.test.utils.http.client.HttpResponse decodingApplicationDTO = + restAPIStore.createApplicationWithCustomAttribute( + decodingApplicationName, "JWT decoding Application", + APIMIntegrationConstants.APPLICATION_TIER.DEFAULT_APP_POLICY_FIFTY_REQ_PER_MIN, + ApplicationDTO.TokenTypeEnum.JWT, attributes); + decodingApplicationId = decodingApplicationDTO.getData(); + + String decodingApiName = "JWTDecodingAPI"; + APIRequest decodingApiRequest = new APIRequest(decodingApiName, decodingApiContext, new URL(endpointURL)); + decodingApiRequest.setVersion(apiVersion); + decodingApiRequest.setVisibility("public"); + decodingApiRequest.setProvider(providerName); + List securitySchemes = new ArrayList<>(); + securitySchemes.add("api_key"); + securitySchemes.add("oauth2"); + securitySchemes.add("oauth_basic_auth_api_key_mandatory"); + decodingApiRequest.setSecurityScheme(securitySchemes); + decodingApiId = createAndPublishAPIUsingRest(decodingApiRequest, restAPIPublisher, false); + restAPIStore.subscribeToAPI(decodingApiId, decodingApplicationId, TIER_GOLD); + ArrayList grantTypes = new ArrayList<>(); + grantTypes.add(APIMIntegrationConstants.GRANT_TYPE.CLIENT_CREDENTIAL); + grantTypes.add(APIMIntegrationConstants.GRANT_TYPE.PASSWORD); + //generate keys + restAPIStore.generateKeys(decodingApplicationId, "36000", "", + ApplicationKeyGenerateRequestDTO.KeyTypeEnum.PRODUCTION, null, grantTypes); + createUser(); + waitForAPIDeploymentSync(user.getUserName(), decodingApiRequest.getName(), decodingApiRequest.getVersion(), + APIMIntegrationConstants.IS_API_EXISTS); + } + + @Test(groups = { "wso2.am" }, description = "Generate keys and invoking custom application") + public void testJWTDecodingforCustomApplication() throws Exception { + + //Call the API with apikey + APIKeyDTO apiKeyDTO = restAPIStore.generateAPIKeys(decodingApplicationId, + ApplicationKeyGenerateRequestDTO.KeyTypeEnum.PRODUCTION.toString(), 36000, null, null); + HttpClient decodingKeyHttpClient = HttpClientBuilder.create().build(); + HttpGet decodingFirstGet = new HttpGet(getAPIInvocationURLHttp(decodingApiContext, apiVersion)); + decodingFirstGet.addHeader("apikey", apiKeyDTO.getApikey()); + HttpResponse decodingFirstResponse = decodingKeyHttpClient.execute(decodingFirstGet); + Assert.assertEquals(decodingFirstResponse.getStatusLine().getStatusCode(), Response.Status.OK.getStatusCode(), + "Response code mismatched when api invocation"); + Thread.sleep(1000); + HttpResponse decodingSecondResponse = decodingKeyHttpClient.execute(decodingFirstGet); + Assert.assertEquals(decodingSecondResponse.getStatusLine().getStatusCode(), Response.Status.OK.getStatusCode(), + "Response code mismatched when api invocation"); + + //Call the API with Bearer token + ApiResponse applicationKeysByKeyType = restAPIStore.getApplicationKeysByKeyType( + decodingApplicationId, ApplicationKeyDTO.KeyTypeEnum.PRODUCTION.getValue()); + ApplicationKeyDTO applicationKeyDTO = applicationKeysByKeyType.getData(); + String accessToken = generateUserToken(applicationKeyDTO.getConsumerKey(), + applicationKeyDTO.getConsumerSecret(), enduserName, enduserPassword); + log.info("Acess Token Generated in JWT ==" + accessToken); + HttpClient decodingTokenHttpClient = HttpClientBuilder.create().build(); + HttpGet decodingThirdGet = new HttpGet(getAPIInvocationURLHttp(decodingApiContext, apiVersion)); + decodingThirdGet.addHeader("Authorization", "Bearer " + accessToken); + HttpResponse decodingThirdResponse = decodingTokenHttpClient.execute(decodingThirdGet); + Assert.assertEquals(decodingThirdResponse.getStatusLine().getStatusCode(), Response.Status.OK.getStatusCode(), + "Response code mismatched when api invocation"); + Thread.sleep(1000); + HttpResponse decodingFourthResponse = decodingTokenHttpClient.execute(decodingThirdGet); + Assert.assertEquals(decodingFourthResponse.getStatusLine().getStatusCode(), Response.Status.OK.getStatusCode(), + "Response code mismatched when api invocation"); + } + + @AfterClass(alwaysRun = true) public void destroy() throws Exception { + userManagementClient.deleteUser(enduserName); + restAPIStore.deleteApplication(decodingApplicationId); + undeployAndDeleteAPIRevisionsUsingRest(decodingApiId, restAPIPublisher); + restAPIPublisher.deleteAPI(decodingApiId); + super.cleanUp(); + + } + + private void createUser() + throws RemoteException, RemoteUserStoreManagerServiceUserStoreExceptionException, UserStoreException { + + String DEFAULT_PROFILE = "default"; + remoteUserStoreManagerServiceClient.addUser(enduserName, enduserPassword, new String[] {}, new ClaimValue[] {}, + DEFAULT_PROFILE, false); + remoteUserStoreManagerServiceClient.setUserClaimValue(enduserName, "http://wso2.org/claims/givenname", + "first name", DEFAULT_PROFILE); + remoteUserStoreManagerServiceClient.setUserClaimValue(enduserName, "http://wso2.org/claims/lastname", + "last name", DEFAULT_PROFILE); + + } + + private String generateUserToken(String consumerKey, String consumerSecret, String enduserName, + String enduserPassword) throws APIManagerIntegrationTestException, JSONException { + + String username = enduserName; + if (userMode != TestUserMode.SUPER_TENANT_ADMIN) { + username = username.concat("@").concat(user.getUserDomain()); + } + String requestBody = "grant_type=password&username=" + username + "&password=" + enduserPassword; + + org.wso2.carbon.automation.test.utils.http.client.HttpResponse httpResponse = restAPIStore.generateUserAccessKey( + consumerKey, consumerSecret, requestBody, tokenEndpointURL); + JSONObject accessTokenGenerationResponse = new JSONObject(httpResponse.getData()); + return accessTokenGenerationResponse.getString("access_token"); + + } +} diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/AM/configFiles/tokenTest/urlSafeTokenTest/deployment.toml b/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/AM/configFiles/tokenTest/urlSafeTokenTest/deployment.toml index fe69c76171..9741be31c6 100755 --- a/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/AM/configFiles/tokenTest/urlSafeTokenTest/deployment.toml +++ b/modules/integration/tests-integration/tests-backend/src/test/resources/artifacts/AM/configFiles/tokenTest/urlSafeTokenTest/deployment.toml @@ -102,3 +102,17 @@ password = "${admin.password}" [apim.sync_runtime_artifacts.gateway.skip_list] apis = ["admin--git2231head_v1.0.0.xml","admin--PizzaShackAPI_v1.0.0.xml","admin--ScriptMediatorAPI_v1.0.xml", "APIThrottleBackendAPI.xml","BackEndSecurity.xml","DigestAuth_API.xml","git2231.xml","HttpPATCHSupport_API.xml","JWKS-Backend.xml","JWTBackendAPI.xml","multiVSR_v1.0.0.xml","Response_API_1.xml","Response_API_2.xml","Response_Custom_API.xml","Response_Error_API.xml","Response_Loc_API.xml","SpecialCRN_v1.0.0.xml","status_code_204_API.xml","stockquote.xml","XML_API.xml","Version1.xml","Version2.xml","schemaValidationAPI.xml"] + +[[apim.devportal.application_attributes]] +required=true +hidden=false +default="Yes" +name="Sandbox access required" +description="Does this Application require access to APIs in the Sandbox environment ? (Yes or No)" + +[[apim.devportal.application_attributes]] +required=true +hidden=false +default="Yes" +name="Production access required" +description="Does this Application require access to APIs in the Production environment ? (Yes or No)" diff --git a/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml b/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml index b2d080143b..2ba09aeadd 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml +++ b/modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml @@ -386,6 +386,7 @@ + From 35991e00e870bb4d564106dd44d2926f2bca00b5 Mon Sep 17 00:00:00 2001 From: Lakith Date: Wed, 29 Nov 2023 14:23:19 +0530 Subject: [PATCH 03/39] Set web socket hostname verification to be enable by default --- .../product/src/main/resources/conf/default.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/distribution/product/src/main/resources/conf/default.json b/modules/distribution/product/src/main/resources/conf/default.json index 9895d593f3..6ec141a678 100644 --- a/modules/distribution/product/src/main/resources/conf/default.json +++ b/modules/distribution/product/src/main/resources/conf/default.json @@ -128,12 +128,13 @@ "transport.netty.https.sender.ssl_profile.read_interval": "600000", "transport.ws.sender.enable": true, "transport.ws.sender.parameters.\u0027ws.custom.header\u0027": "websocket-custom-header", - "transport.ws.sender.parameters.ws.outflow.dispatch.sequence": "outflowDispatchSeq", - "transport.ws.sender.parameters.ws.outflow.dispatch.fault.sequence": "outflowFaultSeq", + "transport.ws.sender.parameters.\u0027ws.outflow.dispatch.sequence\u0027": "outflowDispatchSeq", + "transport.ws.sender.parameters.\u0027ws.outflow.dispatch.fault.sequence\u0027": "outflowFaultSeq", "transport.wss.sender.enable": true, "transport.wss.sender.parameters.\u0027ws.custom.header\u0027": "websocket-custom-header", - "transport.wss.sender.parameters.ws.outflow.dispatch.sequence": "outflowDispatchSeq", - "transport.wss.sender.parameters.ws.outflow.dispatch.fault.sequence": "outflowFaultSeq", + "transport.wss.sender.parameters.\u0027ws.outflow.dispatch.sequence\u0027": "outflowDispatchSeq", + "transport.wss.sender.parameters.\u0027ws.outflow.dispatch.fault.sequence\u0027": "outflowFaultSeq", + "transport.wss.sender.parameters.\u0027ws.client.enable.hostname.verification\u0027": "true", "transport.wss.sender.trust_store.location": "repository/resources/security/$ref{truststore.file_name}", "transport.wss.sender.trust_store.password": "$ref{truststore.password}", "transport.blocking_http.sender.parameters.PROTOCOL": "HTTP/1.1", From 7a26c5a65bd98c6406f40fb42dd8e8791ee344f6 Mon Sep 17 00:00:00 2001 From: Chamila Adhikarinayake Date: Fri, 1 Dec 2023 09:38:01 +0530 Subject: [PATCH 04/39] Remove restart test from executing multiple times --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 37ad00ae68..23f773d441 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -45,7 +45,7 @@ jobs: with: node-version: '10.x' - name: Build product-apim with Test. - run: mvn clean install --file pom.xml -DskipBenchMarkTest=true + run: mvn clean install --file pom.xml -DskipBenchMarkTest=true -DskipRestartTests=true env: PRODUCT_APIM_TEST_GROUPS: ${{ matrix.segment }} - name: Prepare upload testng report From 55de19657497e48258d56d26d58ecffd00cada7f Mon Sep 17 00:00:00 2001 From: Maheshika Goonetilleke Date: Fri, 1 Dec 2023 10:17:12 +0530 Subject: [PATCH 05/39] Update README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index f78abdc9f6..b5ac7b2e87 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,16 @@ For additional support information please refer to http://wso2.com/support For more information on WSO2 API Manager please visit https://wso2.com/api-management/ +Survey On Open Source Community Communication +================================== + +WSO2 wants to learn more about our open source software (OSS) community and your communication preferences to serve you better. + +In addition, we may reach out to a small number of respondents to ask additional questions and offer a small gift. + +Link to survey: https://forms.gle/h5q4M3K7vyXba3bK6 + + Known Issues of WSO2 API Manager ================================== From a2257caabbdfd8019755f39f080d236e918c4b9b Mon Sep 17 00:00:00 2001 From: GihanAyesh Date: Fri, 1 Dec 2023 19:31:55 +0530 Subject: [PATCH 06/39] shared application api key revocation test case --- .../ApplicationSharingTestCase.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/application/groupSharing/ApplicationSharingTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/application/groupSharing/ApplicationSharingTestCase.java index 12196876c3..cf3c85c829 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/application/groupSharing/ApplicationSharingTestCase.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/application/groupSharing/ApplicationSharingTestCase.java @@ -30,6 +30,8 @@ import org.wso2.am.integration.clients.store.api.ApiException; import org.wso2.am.integration.clients.store.api.v1.dto.ApplicationDTO; import org.wso2.am.integration.clients.store.api.v1.dto.ApplicationInfoDTO; +import org.wso2.am.integration.clients.store.api.v1.dto.ApplicationKeyGenerateRequestDTO; +import org.wso2.am.integration.clients.store.api.v1.dto.APIKeyDTO; import org.wso2.am.integration.test.impl.RestAPIStoreImpl; import org.wso2.am.integration.test.utils.UserManagementUtils; import org.wso2.am.integration.test.utils.base.APIMIntegrationBaseTest; @@ -79,8 +81,8 @@ public static Object[][] userModeDataProvider() { public void setEnvironment() throws Exception { super.init(userMode); - createUsersAndApplications(); groups.add(ORGANIZATION); + createUsersAndApplications(); } @Test(groups = "wso2.am", description = "Remove user one's application and check if user two's application also " + @@ -125,6 +127,23 @@ public void testEditApplicationByUserInApplicationGroup() throws ApiException { Assert.assertEquals(serviceResponse.getResponseCode(), HttpStatus.SC_FORBIDDEN); } + @Test(groups = "wso2.am", description = "Generate API key from user 1 and make sure that user 2 can revoke the key") + public void testAPIKeyRevocationBySharedUser() + throws ApiException { + + //Check for application availability + List user1AllAppsList = restAPIStoreClientUser1.getAllApps().getList(); + ApplicationDTO applicationDTO = restAPIStoreClientUser1.getApplicationById(userOneSharedApplicationId); + Assert.assertNotNull(applicationDTO); + Assert.assertEquals(applicationDTO.getName(), SHARED_APPLICATION_NAME); + + //Generate api key by user 1 + APIKeyDTO key = restAPIStoreClientUser1.generateAPIKeys(userOneSharedApplicationId, + ApplicationKeyGenerateRequestDTO.KeyTypeEnum.PRODUCTION.toString(), -1, null, null); + //Revoke api key by user 2 + restAPIStoreClientUser2.revokeAPIKey(userOneSharedApplicationId, key.getApikey()); + } + @AfterClass(alwaysRun = true) public void destroy() throws Exception { restAPIStoreClientUser2.removeApplicationById(userTwoApplicationId); From e2030b447d0d7c3a104dcc3e32399e519a5151ae Mon Sep 17 00:00:00 2001 From: GihanAyesh Date: Mon, 4 Dec 2023 14:47:03 +0530 Subject: [PATCH 07/39] carbon change --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc6deaeecc..8a739d703c 100644 --- a/pom.xml +++ b/pom.xml @@ -1280,7 +1280,7 @@ 9.0.476 - 9.28.190 + 9.28.195 [9.0.0, 10.0.0) From a7fc340c3644f8bbea36918539a924f77975804f Mon Sep 17 00:00:00 2001 From: Savindu Dimal Date: Thu, 14 Sep 2023 12:42:23 +0530 Subject: [PATCH 08/39] Fix loginRequestPath --- .../distribution/product/src/main/extensions/basicauth.jsp | 4 +++- modules/distribution/product/src/main/extensions/login.jsp | 6 +++++- .../repository/conf/tomcat/carbon/WEB-INF/web.xml.j2 | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/distribution/product/src/main/extensions/basicauth.jsp b/modules/distribution/product/src/main/extensions/basicauth.jsp index 5d801ec01b..66b3647813 100644 --- a/modules/distribution/product/src/main/extensions/basicauth.jsp +++ b/modules/distribution/product/src/main/extensions/basicauth.jsp @@ -93,14 +93,16 @@ if (userName.value) { let contextPath = "<%=proxyContextPath%>" + let loginRequestPath = "<%=loginContextRequestUrl%>" if (contextPath !== "") { contextPath = contextPath.startsWith('/') ? contextPath : "/" + contextPath contextPath = contextPath.endsWith('/') ? contextPath.substring(0, contextPath.length - 1) : contextPath + loginRequestPath = loginRequestPath.startsWith('../') ? loginRequestPath.substring(2, loginRequestPath.length) : loginRequestPath } $.ajax({ type: "GET", - url: contextPath + "<%=loginContextRequestUrl%>", + url: contextPath + loginRequestPath, xhrFields: { withCredentials: true }, success: function (data) { if (data && data.status == 'redirect' && data.redirectUrl && data.redirectUrl.length > 0) { diff --git a/modules/distribution/product/src/main/extensions/login.jsp b/modules/distribution/product/src/main/extensions/login.jsp index b9523b1ac1..594bfa9bf9 100644 --- a/modules/distribution/product/src/main/extensions/login.jsp +++ b/modules/distribution/product/src/main/extensions/login.jsp @@ -501,9 +501,13 @@