From 8a0ffd713be658cd0ce90f27f648b4e4ec793e77 Mon Sep 17 00:00:00 2001 From: Thisal Tennakoon Date: Tue, 19 Sep 2023 14:59:19 +0530 Subject: [PATCH 1/2] Added integration test for API Key authentication for WebSocket API --- ...ocketAPIInvocationWithTracingTestCase.java | 8 +- .../tests/websocket/WebSocketAPITestCase.java | 239 ++++++++++++++++-- 2 files changed, 225 insertions(+), 22 deletions(-) diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPIInvocationWithTracingTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPIInvocationWithTracingTestCase.java index 3f84de3b67..50871d351b 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPIInvocationWithTracingTestCase.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPIInvocationWithTracingTestCase.java @@ -219,8 +219,8 @@ public void testWebSocketAPIInvocation() throws Exception { consumerSecret = applicationKeyDTO.getConsumerSecret(); WebSocketClient client = new WebSocketClient(); try { - invokeAPI(client, tokenJti, WebSocketAPITestCase.AUTH_IN.HEADER, null, apiEndPoint); - invokeAPI(client, tokenJti, WebSocketAPITestCase.AUTH_IN.QUERY, null, apiEndPoint); + invokeAPI(client, tokenJti, WebSocketAPITestCase.AUTH_IN.OAUTH_HEADER, null, apiEndPoint); + invokeAPI(client, tokenJti, WebSocketAPITestCase.AUTH_IN.OAUTH_QUERY, null, apiEndPoint); } catch (Exception e) { log.error("Exception in connecting to server", e); Assert.fail("Client cannot connect to server"); @@ -278,10 +278,10 @@ private void invokeAPI(WebSocketClient client, String accessToken, WebSocketAPIT ClientUpgradeRequest request = new ClientUpgradeRequest(); URI echoUri = null; - if (WebSocketAPITestCase.AUTH_IN.HEADER == in) { + if (WebSocketAPITestCase.AUTH_IN.OAUTH_HEADER == in) { request.setHeader("Authorization", "Bearer " + accessToken); echoUri = new URI(apiEndPoint); - } else if (WebSocketAPITestCase.AUTH_IN.QUERY == in) { + } else if (WebSocketAPITestCase.AUTH_IN.OAUTH_QUERY == in) { echoUri = new URI(apiEndPoint + "?access_token=" + accessToken); } diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPITestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPITestCase.java index acb5eb3592..4f4cf3202f 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPITestCase.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPITestCase.java @@ -22,7 +22,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaders; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -48,6 +47,7 @@ import org.wso2.am.integration.clients.publisher.api.v1.dto.APIListDTO; 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.APIKeyDTO; import org.wso2.am.integration.clients.store.api.v1.dto.ApplicationKeyGenerateRequestDTO; import org.wso2.am.integration.clients.store.api.v1.dto.SubscriptionDTO; import org.wso2.am.integration.test.impl.DtoFactory; @@ -64,7 +64,6 @@ import org.wso2.carbon.apimgt.api.model.APIIdentifier; import org.wso2.carbon.automation.engine.annotations.ExecutionEnvironment; import org.wso2.carbon.automation.engine.annotations.SetEnvironment; -import org.wso2.carbon.automation.engine.context.AutomationContext; import org.wso2.carbon.automation.engine.context.TestUserMode; import org.wso2.carbon.automation.engine.frameworkutils.FrameworkPathUtil; import org.wso2.carbon.automation.test.utils.common.TestConfigurationProvider; @@ -74,15 +73,12 @@ import java.io.File; import java.io.FileInputStream; -import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; -import java.net.Socket; import java.net.URI; import java.net.URL; import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Calendar; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -97,8 +93,10 @@ public class WebSocketAPITestCase extends APIMIntegrationBaseTest { private final Log log = LogFactory.getLog(WebSocketAPITestCase.class); enum AUTH_IN { - HEADER, - QUERY + OAUTH_HEADER, + OAUTH_QUERY, + APIKEY_HEADER, + APIKEY_QUERY } private final ExecutorService executorService = Executors.newSingleThreadExecutor(); private final String apiName = "WebSocketAPI"; @@ -131,6 +129,8 @@ enum AUTH_IN { long throttleMarkTime = 0; String apiVersion2 = "2.0.0"; String endPointApplication = "EndPointApplication"; + ArrayList securityScheme = new ArrayList<>(); + String apiKey = "api_key"; @Factory(dataProvider = "userModeDataProvider") public WebSocketAPITestCase(TestUserMode userMode) { @@ -237,13 +237,15 @@ public void testWebSocketAPIInvocation() throws Exception { applicationKeyDTO = restAPIStore.generateKeys(appId, "3600", null, ApplicationKeyGenerateRequestDTO.KeyTypeEnum.PRODUCTION, null, grantTypes); String accessToken = applicationKeyDTO.getToken().getAccessToken(); - String tokenJti = TokenUtils.getJtiOfJwtToken(accessToken); + String opaqueToken = TokenUtils.getJtiOfJwtToken(accessToken); consumerKey = applicationKeyDTO.getConsumerKey(); consumerSecret = applicationKeyDTO.getConsumerSecret(); WebSocketClient client = new WebSocketClient(); try { - invokeAPI(client, tokenJti, AUTH_IN.HEADER, null, apiEndPoint); - invokeAPI(client, tokenJti, AUTH_IN.QUERY, null, apiEndPoint); + invokeAPI(client, accessToken, AUTH_IN.OAUTH_HEADER, null, apiEndPoint); + invokeAPI(client, accessToken, AUTH_IN.OAUTH_QUERY, null, apiEndPoint); + invokeAPI(client, opaqueToken, AUTH_IN.OAUTH_HEADER, null, apiEndPoint); + invokeAPI(client, opaqueToken, AUTH_IN.OAUTH_QUERY, null, apiEndPoint); } catch (Exception e) { log.error("Exception in connecting to server", e); Assert.fail("Client cannot connect to server"); @@ -274,8 +276,8 @@ public void testWebSocketAPIInvocationWithJWTToken() throws Exception { //consumerSecret = applicationKeyDTO.getConsumerSecret(); WebSocketClient client = new WebSocketClient(); try { - invokeAPI(client, accessToken, AUTH_IN.HEADER, null, apiEndPoint); - invokeAPI(client, accessToken, AUTH_IN.QUERY, null, apiEndPoint); + invokeAPI(client, accessToken, AUTH_IN.OAUTH_HEADER, null, apiEndPoint); + invokeAPI(client, accessToken, AUTH_IN.OAUTH_QUERY, null, apiEndPoint); } catch (Exception e) { log.error("Exception in connecting to server", e); Assert.fail("Client cannot connect to server"); @@ -340,8 +342,8 @@ public void testWebSocketAPIRemoveEndpoint() throws Exception { WebSocketClient client0 = new WebSocketClient(); try { - invokeAPI(client0, sandboxAccessToken, AUTH_IN.HEADER, null, apiEndPoint); - invokeAPI(client0, sandboxAccessToken, AUTH_IN.QUERY, null, apiEndPoint); + invokeAPI(client0, sandboxAccessToken, AUTH_IN.OAUTH_HEADER, null, apiEndPoint); + invokeAPI(client0, sandboxAccessToken, AUTH_IN.OAUTH_QUERY, null, apiEndPoint); Assert.assertTrue(true, "Client can connect to the sandbox endpoint"); } catch (Exception e) { log.error("Exception in connecting to server", e); @@ -356,7 +358,7 @@ public void testWebSocketAPIRemoveEndpoint() throws Exception { String prodAccessToken = prodApplicationKeyDTO.getToken().getAccessToken(); WebSocketClient client1 = new WebSocketClient(); try { - invokeAPI(client1, prodAccessToken, AUTH_IN.QUERY, null, apiEndPoint); + invokeAPI(client1, prodAccessToken, AUTH_IN.OAUTH_QUERY, null, apiEndPoint); Assert.fail("Client can connect to the production endpoint when production endpoint is not configured"); } catch (Exception e) { log.debug("Exception in connecting to server", e); @@ -459,7 +461,8 @@ public void testWebSocketAPIInvalidTokenInvocation() throws Exception { WebSocketClient client = new WebSocketClient(); boolean apiInvocationFailed = false; try { - invokeAPI(client, "00000000-0000-0000-0000-000000000000", AUTH_IN.HEADER, null, apiEndPoint); + invokeAPI(client, "00000000-0000-0000-0000-000000000000", AUTH_IN.OAUTH_HEADER, + null, apiEndPoint); } catch (APIManagerIntegrationTestException e) { log.error("Exception in connecting to server", e); apiInvocationFailed = true; @@ -475,6 +478,201 @@ public void testWebSocketAPIInvalidTokenInvocation() throws Exception { } } + @Test(description = "Invoke API using API key when API Key authentication is not enabled", + dependsOnMethods = "testWebSocketAPIInvalidTokenInvocation") + public void testWebSocketAPIInvocationUsingAPIKeyWhenAPIKeyAuthenticationDisabled() throws Exception { + + APIKeyDTO apiKeyDTO = restAPIStore.generateAPIKeys(appId, ApplicationKeyGenerateRequestDTO.KeyTypeEnum. + PRODUCTION.toString(), -1, null, null); + String accessToken = apiKeyDTO.getApikey(); + WebSocketClient client = new WebSocketClient(); + boolean apiInvocationFailed = false; + try { + invokeAPI(client, accessToken, AUTH_IN.APIKEY_HEADER, null, apiEndPoint); + } catch (APIManagerIntegrationTestException e) { + apiInvocationFailed = true; + assertTrue(true, "Exception in connecting to server because API Key authentication is not enabled"); + } catch (Exception e) { + log.error("Exception in connecting to server", e); + Assert.fail("Client cannot connect to server"); + } finally { + if (!apiInvocationFailed) { + Assert.fail("WS API was invoked using API key when API Key authentication is not enabled"); + } + client.stop(); + } + } + + @Test(description = "Invoke API using API key", + dependsOnMethods = "testWebSocketAPIInvocationUsingAPIKeyWhenAPIKeyAuthenticationDisabled") + public void testWebSocketAPIInvocationUsingAPIKey() throws Exception { + + // Update API to enable API Key authentication + HttpResponse response = restAPIPublisher.getAPI(websocketAPIID); + Gson g = new Gson(); + APIDTO apidto = g.fromJson(response.getData(), APIDTO.class); + securityScheme.add(apiKey); + apidto.setSecurityScheme(securityScheme); + restAPIPublisher.updateAPI(apidto); + Thread.sleep(1000); // Delay is needed to propagate changes to the components + APIKeyDTO apiKeyDTO = restAPIStore.generateAPIKeys(appId, ApplicationKeyGenerateRequestDTO.KeyTypeEnum. + PRODUCTION.toString(), -1, null, null); + String accessToken = apiKeyDTO.getApikey(); + WebSocketClient client = new WebSocketClient(); + try { + invokeAPI(client, accessToken, AUTH_IN.APIKEY_HEADER, null, apiEndPoint); + invokeAPI(client, accessToken, AUTH_IN.APIKEY_QUERY, null, apiEndPoint); + } catch (Exception e) { + log.error("Exception in connecting to server", e); + Assert.fail("Client cannot connect to server"); + } finally { + client.stop(); + } + } + + @Test(description = "Invoke API using OAuth access token when OAuth authentication is not enabled", + dependsOnMethods = "testWebSocketAPIInvocationUsingAPIKey") + public void testWebSocketAPIInvocationUsingOAuthWhenOAuthAuthenticationDisabled() throws Exception { + + WebSocketClient client = new WebSocketClient(); + boolean apiInvocationFailed = false; + try { + invokeAPI(client, applicationKeyDTO.getToken().getAccessToken(), AUTH_IN.OAUTH_HEADER, + null, apiEndPoint); + } catch (APIManagerIntegrationTestException e) { + apiInvocationFailed = true; + assertTrue(true, "Exception in connecting to server because OAuth authentication is not enabled"); + } catch (Exception e) { + log.error("Exception in connecting to server", e); + Assert.fail("Client cannot connect to server"); + } finally { + if (!apiInvocationFailed) { + Assert.fail("WS API was invoked using OAuth access token when OAuth authentication is not enabled"); + } + client.stop(); + } + } + + @Test(description = "Invoke API using Expired API key", + dependsOnMethods = "testWebSocketAPIInvocationUsingOAuthWhenOAuthAuthenticationDisabled") + public void testWebSocketAPIInvocationUsingExpiredAPIKey() throws Exception { + + APIKeyDTO apiKeyDTO = restAPIStore.generateAPIKeys(appId, ApplicationKeyGenerateRequestDTO.KeyTypeEnum. + PRODUCTION.toString(), 1, null, null); + String accessToken = apiKeyDTO.getApikey(); + WebSocketClient client = new WebSocketClient(); + boolean apiInvocationFailed = false; + try { + Thread.sleep(2000); + invokeAPI(client, accessToken, AUTH_IN.APIKEY_HEADER, null, apiEndPoint); + } catch (APIManagerIntegrationTestException e) { + apiInvocationFailed = true; + assertTrue(true, "Exception in connecting to server because the API Key is expired"); + } catch (Exception e) { + log.error("Exception in connecting to server", e); + Assert.fail("Client cannot connect to server"); + } finally { + if (!apiInvocationFailed) { + Assert.fail("WS API was invoked with an expired API key"); + } + client.stop(); + } + } + + @Test(description = "Invoke API using API key generated using IP restrictions", + dependsOnMethods = "testWebSocketAPIInvocationUsingExpiredAPIKey") + public void testWebSocketAPIInvocationUsingAPIKeyGeneratedUsingIPRestrictions() throws Exception { + + APIKeyDTO apiKeyDTO = restAPIStore.generateAPIKeys(appId, ApplicationKeyGenerateRequestDTO.KeyTypeEnum. + PRODUCTION.toString(), -1, "192.168.1.2, 152.12.0.0/13, 2002:eb8::2, 1001:ab8::/44," + + " 127.0.0.1", null); + String accessToken = apiKeyDTO.getApikey(); + WebSocketClient client = new WebSocketClient(); + try { + invokeAPI(client, accessToken, AUTH_IN.APIKEY_HEADER, null, apiEndPoint); + } catch (Exception e) { + log.error("Exception in connecting to server", e); + Assert.fail("Client cannot connect to server"); + } finally { + client.stop(); + } + } + + @Test(description = "Invoke API using an API key restricted for another IP", + dependsOnMethods = "testWebSocketAPIInvocationUsingAPIKeyGeneratedUsingIPRestrictions") + public void testWebSocketAPIInvocationUsingAPIKeyRestrictedForAnotherIP() throws Exception { + + APIKeyDTO apiKeyDTO = restAPIStore.generateAPIKeys(appId, ApplicationKeyGenerateRequestDTO.KeyTypeEnum. + PRODUCTION.toString(), -1, "192.168.1.2, 152.12.0.0/13, 2002:eb8::2, 1001:ab8::/44," + + " 1.1.1.1", null); + String accessToken = apiKeyDTO.getApikey(); + WebSocketClient client = new WebSocketClient(); + boolean apiInvocationFailed = false; + try { + invokeAPI(client, accessToken, AUTH_IN.APIKEY_HEADER, null, apiEndPoint); + } catch (APIManagerIntegrationTestException e) { + apiInvocationFailed = true; + assertTrue(true, "Client cannot connect to server because the API Key is restricted for another IP"); + } catch (Exception e) { + log.error("Exception in connecting to server", e); + Assert.fail("Client cannot connect to server"); + } finally { + if (!apiInvocationFailed) { + Assert.fail("WS API was invoked using API key restricted for another IP"); + } + client.stop(); + } + } + + @Test(description = "Invoke API using API key generated using Referer restrictions", + dependsOnMethods = "testWebSocketAPIInvocationUsingAPIKeyRestrictedForAnotherIP") + public void testWebSocketAPIInvocationUsingAPIKeyGeneratedUsingRefererRestrictions() throws Exception { + + APIKeyDTO apiKeyDTO = restAPIStore.generateAPIKeys(appId, ApplicationKeyGenerateRequestDTO.KeyTypeEnum. + PRODUCTION.toString(), -1, null, "www.example.com/path, " + + "sub.example.com/*, *.example.com/*, www.wso2.com"); + HttpHeaders headers = new DefaultHttpHeaders(); + headers.add("Referer", "www.wso2.com"); + String accessToken = apiKeyDTO.getApikey(); + WebSocketClient client = new WebSocketClient(); + try { + invokeAPI(client, accessToken, AUTH_IN.APIKEY_HEADER, headers, apiEndPoint); + } catch (Exception e) { + log.error("Exception in connecting to server", e); + Assert.fail("Client cannot connect to server"); + } finally { + client.stop(); + } + } + + @Test(description = "Invoke API using API key restricted for another Referer", + dependsOnMethods = "testWebSocketAPIInvocationUsingAPIKeyGeneratedUsingRefererRestrictions") + public void testWebSocketAPIInvocationUsingAPIKeyGeneratedForAnotherReferer() throws Exception { + + APIKeyDTO apiKeyDTO = restAPIStore.generateAPIKeys(appId, ApplicationKeyGenerateRequestDTO.KeyTypeEnum. + PRODUCTION.toString(), -1, null, "www.example.com/path, " + + "sub.example.com/*, *.example.com/*, www.wso2.com"); + HttpHeaders headers = new DefaultHttpHeaders(); + headers.add("Referer", "www.wso2.org"); + String accessToken = apiKeyDTO.getApikey(); + WebSocketClient client = new WebSocketClient(); + boolean apiInvocationFailed = false; + try { + invokeAPI(client, accessToken, AUTH_IN.APIKEY_HEADER, null, apiEndPoint); + } catch (APIManagerIntegrationTestException e) { + apiInvocationFailed = true; + assertTrue(true, "Client cannot connect to server because the API Key is restricted for another Referer"); + } catch (Exception e) { + log.error("Exception in connecting to server", e); + Assert.fail("Client cannot connect to server"); + } finally { + if (!apiInvocationFailed) { + Assert.fail("WS API was invoked using API key generated for another Referer"); + } + client.stop(); + } + } + /** * Wait for client to receive reply from the server * @@ -594,11 +792,16 @@ private void invokeAPI(WebSocketClient client, String accessToken, AUTH_IN in, H ClientUpgradeRequest request = new ClientUpgradeRequest(); URI echoUri = null; - if (AUTH_IN.HEADER == in) { + if (AUTH_IN.OAUTH_HEADER == in) { request.setHeader("Authorization", "Bearer " + accessToken); echoUri = new URI(apiEndPoint); - } else if (AUTH_IN.QUERY == in) { + } else if (AUTH_IN.OAUTH_QUERY == in) { echoUri = new URI(apiEndPoint + "?access_token=" + accessToken); + } else if (AUTH_IN.APIKEY_HEADER == in) { + request.setHeader("apikey", accessToken); + echoUri = new URI(apiEndPoint); + } else if (AUTH_IN.APIKEY_QUERY == in) { + echoUri = new URI(apiEndPoint + "?apikey=" + accessToken); } if (optionalRequestHeaders != null) { From 5cc388a94bce4a5c4c0e94e006feea3ebafdd4d9 Mon Sep 17 00:00:00 2001 From: Thisal Tennakoon Date: Thu, 1 Feb 2024 19:44:41 +0530 Subject: [PATCH 2/2] Update WebSocketAPITestCase.java --- .../am/integration/tests/websocket/WebSocketAPITestCase.java | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPITestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPITestCase.java index 72f1939f99..12f331af56 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPITestCase.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/am/integration/tests/websocket/WebSocketAPITestCase.java @@ -665,6 +665,7 @@ public void testWebSocketAPIInvocationUsingAPIKeyGeneratedForAnotherReferer() th } client.stop(); } + } @Test(description = "Create WebSocket API with malformed context", dependsOnMethods = "testWebSocketAPIRemoveEndpoint")