From 0f60df4dbc81c06479e36913be9e79a6bbd443a8 Mon Sep 17 00:00:00 2001 From: Maduranga Siriwardena Date: Thu, 14 Nov 2024 16:50:29 +0530 Subject: [PATCH 1/2] Enable auto redirect for the http client --- .../auth/PasswordlessSMSOTPAuthTestCase.java | 56 ++++--------- .../test/base/MockClientCallback.java | 81 +++++++++++++++++++ .../test/base/MockOIDCIdentityProvider.java | 1 + .../test/base/MockSMSProvider.java | 3 +- 4 files changed, 99 insertions(+), 42 deletions(-) create mode 100644 modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockClientCallback.java diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/auth/PasswordlessSMSOTPAuthTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/auth/PasswordlessSMSOTPAuthTestCase.java index c7ca4b0d84..0f0fcd6f94 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/auth/PasswordlessSMSOTPAuthTestCase.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/auth/PasswordlessSMSOTPAuthTestCase.java @@ -21,16 +21,14 @@ import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; -import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.config.Lookup; import org.apache.http.config.RegistryBuilder; import org.apache.http.cookie.CookieSpecProvider; -import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.LaxRedirectStrategy; import org.apache.http.impl.cookie.RFC6265CookieSpecProvider; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicNameValuePair; @@ -41,6 +39,7 @@ import org.testng.annotations.Factory; import org.testng.annotations.Test; import org.wso2.carbon.automation.engine.context.TestUserMode; +import org.wso2.identity.integration.test.base.MockClientCallback; import org.wso2.identity.integration.test.base.MockSMSProvider; import org.wso2.identity.integration.test.oidc.OIDCAbstractIntegrationTest; import org.wso2.identity.integration.test.oidc.OIDCUtilTest; @@ -59,8 +58,6 @@ import org.wso2.identity.integration.test.utils.OAuth2Constant; import java.io.IOException; -import java.net.URI; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -93,6 +90,8 @@ public class PasswordlessSMSOTPAuthTestCase extends OIDCAbstractIntegrationTest private String authorizationCode; private MockSMSProvider mockSMSProvider; + private MockClientCallback mockClientCallback; + private TestUserMode userMode; @Factory(dataProvider = "testExecutionContextProvider") @@ -116,6 +115,10 @@ public void testInit() throws Exception { super.init(userMode); mockSMSProvider = new MockSMSProvider(); mockSMSProvider.start(); + + mockClientCallback = new MockClientCallback(); + mockClientCallback.start(); + super.init(); Lookup cookieSpecRegistry = RegistryBuilder.create() @@ -127,13 +130,8 @@ public void testInit() throws Exception { client = HttpClientBuilder.create() .setDefaultRequestConfig(requestConfig) .setDefaultCookieSpecRegistry(cookieSpecRegistry) - .setRedirectStrategy(new DefaultRedirectStrategy() { - @Override - protected boolean isRedirectable(String method) { - - return false; - } - }).build(); + .setRedirectStrategy(new LaxRedirectStrategy()) + .build(); backendURL = backendURL.replace("services/", ""); @@ -172,6 +170,7 @@ public void atEnd() throws Exception { scim2RestClient.closeHttpClient(); mockSMSProvider.stop(); + mockClientCallback.stop(); } @Test(groups = "wso2.is", description = "Test passwordless authentication with SMS OTP") @@ -190,19 +189,13 @@ private void sendAuthorizeRequest() throws Exception { List urlParameters = new ArrayList<>(); urlParameters.add(new BasicNameValuePair("response_type", OAuth2Constant.OAUTH2_GRANT_TYPE_CODE)); urlParameters.add(new BasicNameValuePair("client_id", oidcApplication.getClientId())); - urlParameters.add(new BasicNameValuePair("redirect_uri", OAuth2Constant.CALLBACK_URL)); + urlParameters.add(new BasicNameValuePair("redirect_uri", MockClientCallback.CALLBACK_URL)); urlParameters.add(new BasicNameValuePair("scope", "openid")); HttpResponse response = sendPostRequestWithParameters(client, urlParameters, getTenantQualifiedURL(AUTHORIZE_ENDPOINT_URL, tenantInfo.getDomain())); - Header locationHeader = response.getFirstHeader(OAuth2Constant.HTTP_RESPONSE_HEADER_LOCATION); - assertNotNull(locationHeader, "Location header for authorize request"); - EntityUtils.consume(response.getEntity()); - - response = sendGetRequest(client, locationHeader.getValue()); - Map keyPositionMap = new HashMap<>(1); keyPositionMap.put("name=\"sessionDataKey\"", 1); List keyValues = DataExtractUtil.extractDataFromResponse(response, keyPositionMap); @@ -218,16 +211,7 @@ private void performUserLogin() throws Exception { sendLoginPostForIdentifier(client, sessionDataKey, userObject.getUserName()); HttpResponse response = sendLoginPostForOtp(client, sessionDataKey, mockSMSProvider.getOTP()); - Header locationHeader = response.getFirstHeader(OAuth2Constant.HTTP_RESPONSE_HEADER_LOCATION); - assertNotNull(locationHeader, "Location header"); - EntityUtils.consume(response.getEntity()); - - response = sendGetRequest(client, locationHeader.getValue()); - locationHeader = response.getFirstHeader(OAuth2Constant.HTTP_RESPONSE_HEADER_LOCATION); - assertNotNull(locationHeader, "Redirection URL to the application with authorization code"); - EntityUtils.consume(response.getEntity()); - - authorizationCode = getAuthorizationCodeFromURL(locationHeader.getValue()); + authorizationCode = EntityUtils.toString(response.getEntity()); assertNotNull(authorizationCode); } @@ -256,7 +240,7 @@ private HttpResponse sendTokenRequestForCodeGrant() throws Exception { List urlParameters = new ArrayList<>(); urlParameters.add(new BasicNameValuePair("code", authorizationCode)); urlParameters.add(new BasicNameValuePair("grant_type", OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE)); - urlParameters.add(new BasicNameValuePair("redirect_uri", OAuth2Constant.CALLBACK_URL)); + urlParameters.add(new BasicNameValuePair("redirect_uri", MockClientCallback.CALLBACK_URL)); urlParameters.add(new BasicNameValuePair("client_id", oidcApplication.getClientSecret())); urlParameters.add(new BasicNameValuePair("scope", "openid")); @@ -272,21 +256,11 @@ private HttpResponse sendTokenRequestForCodeGrant() throws Exception { getTenantQualifiedURL(ACCESS_TOKEN_ENDPOINT, tenantInfo.getDomain())); } - private String getAuthorizationCodeFromURL(String location) { - - URI uri = URI.create(location); - return URLEncodedUtils.parse(uri, StandardCharsets.UTF_8).stream() - .filter(param -> "code".equals(param.getName())) - .map(NameValuePair::getValue) - .findFirst() - .orElse(null); - } - private OIDCApplication initOIDCApplication() { OIDCApplication playgroundApp = new OIDCApplication(OIDCUtilTest.playgroundAppOneAppName, OIDCUtilTest.playgroundAppOneAppContext, - OAuth2Constant.CALLBACK_URL); + MockClientCallback.CALLBACK_URL); return playgroundApp; } diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockClientCallback.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockClientCallback.java new file mode 100644 index 0000000000..2e7ca094cc --- /dev/null +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockClientCallback.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * 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.identity.integration.test.base; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; +import org.wso2.identity.integration.common.utils.ISIntegrationTest; +import org.wso2.identity.integration.test.util.Utils; + +import java.nio.file.Paths; +import java.util.concurrent.atomic.AtomicReference; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.matching; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; + +/** + * Mock client callback endpoint to test OIDC related flows. + */ +public class MockClientCallback { + + public static final String CALLBACK_URL = "https://localhost:8091/dummyApp/oauth2client"; + + private WireMockServer wireMockServer; + + public void start() { + + wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig() + .httpsPort(8091) + .httpDisabled(true) + .keystorePath(Paths.get(Utils.getResidentCarbonHome(), "repository", "resources", "security", + ISIntegrationTest.KEYSTORE_NAME).toAbsolutePath().toString()) + .keystorePassword("wso2carbon") + .keyManagerPassword("wso2carbon") + .extensions(new ResponseTemplateTransformer(null, true, null, null))); + + wireMockServer.start(); + + // Configure the mock client endpoints. + configureMockEndpoints(); + } + + public void stop() { + + if (wireMockServer != null) { + wireMockServer.stop(); + } + } + + private void configureMockEndpoints() { + + try { + wireMockServer.stubFor(get(urlPathEqualTo("/dummyApp/oauth2client")) + .withQueryParam("code", matching(".*")) + .willReturn(aResponse() + .withBody("{{request.query.code}}") + .withTransformers("response-template") + .withStatus(200))); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockOIDCIdentityProvider.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockOIDCIdentityProvider.java index ed2b7051a5..b3e985570f 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockOIDCIdentityProvider.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockOIDCIdentityProvider.java @@ -71,6 +71,7 @@ public void start() { wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig() .httpsPort(8089) + .httpDisabled(true) .keystorePath(Paths.get(Utils.getResidentCarbonHome(), "repository", "resources", "security", ISIntegrationTest.KEYSTORE_NAME).toAbsolutePath().toString()) .keystorePassword("wso2carbon") diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockSMSProvider.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockSMSProvider.java index 7fa1693a5a..646c131c0b 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockSMSProvider.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockSMSProvider.java @@ -53,6 +53,7 @@ public void start() { wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig() .httpsPort(8090) + .httpDisabled(true) .keystorePath(Paths.get(Utils.getResidentCarbonHome(), "repository", "resources", "security", ISIntegrationTest.KEYSTORE_NAME).toAbsolutePath().toString()) .keystorePassword("wso2carbon") @@ -93,7 +94,7 @@ public String getName() { wireMockServer.start(); - // Configure the mock OIDC endpoints. + // Configure the mock SMS endpoints. configureMockEndpoints(); } From 0cac5a37b2c998c3c8c60ad04e481468ad3733c9 Mon Sep 17 00:00:00 2001 From: Maduranga Siriwardena Date: Thu, 21 Nov 2024 15:14:43 +0530 Subject: [PATCH 2/2] Change code retrieval method --- .../auth/PasswordlessSMSOTPAuthTestCase.java | 3 +- .../test/base/MockClientCallback.java | 34 +++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/auth/PasswordlessSMSOTPAuthTestCase.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/auth/PasswordlessSMSOTPAuthTestCase.java index 0f0fcd6f94..527dad0a3f 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/auth/PasswordlessSMSOTPAuthTestCase.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/auth/PasswordlessSMSOTPAuthTestCase.java @@ -210,8 +210,9 @@ private void performUserLogin() throws Exception { sendLoginPostForIdentifier(client, sessionDataKey, userObject.getUserName()); HttpResponse response = sendLoginPostForOtp(client, sessionDataKey, mockSMSProvider.getOTP()); + EntityUtils.consume(response.getEntity()); - authorizationCode = EntityUtils.toString(response.getEntity()); + authorizationCode = mockClientCallback.getAuthorizationCode(); assertNotNull(authorizationCode); } diff --git a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockClientCallback.java b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockClientCallback.java index 2e7ca094cc..955f56ba48 100644 --- a/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockClientCallback.java +++ b/modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/base/MockClientCallback.java @@ -20,7 +20,10 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.extension.ResponseTransformerV2; import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; +import com.github.tomakehurst.wiremock.http.Response; +import com.github.tomakehurst.wiremock.stubbing.ServeEvent; import org.wso2.identity.integration.common.utils.ISIntegrationTest; import org.wso2.identity.integration.test.util.Utils; @@ -39,6 +42,8 @@ public class MockClientCallback { public static final String CALLBACK_URL = "https://localhost:8091/dummyApp/oauth2client"; + private final AtomicReference authorizationCode = new AtomicReference<>(); + private WireMockServer wireMockServer; public void start() { @@ -50,7 +55,26 @@ public void start() { ISIntegrationTest.KEYSTORE_NAME).toAbsolutePath().toString()) .keystorePassword("wso2carbon") .keyManagerPassword("wso2carbon") - .extensions(new ResponseTemplateTransformer(null, true, null, null))); + .extensions(new ResponseTemplateTransformer(null, true, null, null), + new ResponseTransformerV2() { + + @Override + public Response transform(Response response, ServeEvent serveEvent) { + + authorizationCode.set(serveEvent.getRequest().getQueryParams().get("code").firstValue()); + return response; + } + + @Override + public boolean applyGlobally() { + return false; + } + + @Override + public String getName() { + return "authz-code-transformer"; + } + })); wireMockServer.start(); @@ -71,11 +95,15 @@ private void configureMockEndpoints() { wireMockServer.stubFor(get(urlPathEqualTo("/dummyApp/oauth2client")) .withQueryParam("code", matching(".*")) .willReturn(aResponse() - .withBody("{{request.query.code}}") - .withTransformers("response-template") + .withTransformers("response-template", "authz-code-transformer") .withStatus(200))); } catch (Exception e) { throw new RuntimeException(e); } } + + public String getAuthorizationCode() { + + return authorizationCode.get(); + } }