diff --git a/components/org.wso2.carbon.identity.api.user.recovery/src/main/java/org/wso2/carbon/identity/recovery/endpoint/Utils/RecoveryUtil.java b/components/org.wso2.carbon.identity.api.user.recovery/src/main/java/org/wso2/carbon/identity/recovery/endpoint/Utils/RecoveryUtil.java index 1c76ecd912..9d090d0801 100644 --- a/components/org.wso2.carbon.identity.api.user.recovery/src/main/java/org/wso2/carbon/identity/recovery/endpoint/Utils/RecoveryUtil.java +++ b/components/org.wso2.carbon.identity.api.user.recovery/src/main/java/org/wso2/carbon/identity/recovery/endpoint/Utils/RecoveryUtil.java @@ -470,7 +470,7 @@ private static Properties validateCaptchaConfigs(Properties properties) { RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants.RE_CAPTCHA_VERIFY_URL), Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); } - // Check if project id is available for reCaptcha Enterprise + // Check if project id is available for reCaptcha Enterprise. if (CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE.equals(reCaptchaType) && StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_PROJECT_ID))) { RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants diff --git a/components/org.wso2.carbon.identity.api.user.recovery/src/main/java/org/wso2/carbon/identity/recovery/endpoint/impl/CaptchaApiServiceImpl.java b/components/org.wso2.carbon.identity.api.user.recovery/src/main/java/org/wso2/carbon/identity/recovery/endpoint/impl/CaptchaApiServiceImpl.java index 50d928c8ba..fd96ce7afa 100644 --- a/components/org.wso2.carbon.identity.api.user.recovery/src/main/java/org/wso2/carbon/identity/recovery/endpoint/impl/CaptchaApiServiceImpl.java +++ b/components/org.wso2.carbon.identity.api.user.recovery/src/main/java/org/wso2/carbon/identity/recovery/endpoint/impl/CaptchaApiServiceImpl.java @@ -20,7 +20,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpEntity; @@ -90,42 +89,28 @@ public Response verifyCaptcha(ReCaptchaResponseTokenDTO reCaptchaResponse, Strin HttpEntity entity = response.getEntity(); ReCaptchaVerificationResponseDTO reCaptchaVerificationResponseDTO = new ReCaptchaVerificationResponseDTO(); - if (CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE.equals(reCaptchaType)) { - // For ReCaptcha Enterprise. - if (entity == null) { - RecoveryUtil.handleBadRequest("ReCaptcha Enterprise verification response is not received.", - Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } - try { - try (InputStream in = entity.getContent()) { - JsonObject verificationResponse = new JsonParser().parse(IOUtils.toString(in)).getAsJsonObject(); - JsonObject tokenProperties = verificationResponse.get(CaptchaConstants.CAPTCHA_TOKEN_PROPERTIES) - .getAsJsonObject(); - boolean success = tokenProperties.get(CaptchaConstants.CAPTCHA_VALID).getAsBoolean(); - reCaptchaVerificationResponseDTO.setSuccess(success); - } - } catch (IOException e) { - log.error("Unable to read the verification response.", e); - RecoveryUtil.handleBadRequest("Unable to read the verification response.", - Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } - } else { - // For ReCaptcha v2 and v3. - try { - if (entity == null) { - RecoveryUtil.handleBadRequest("ReCaptcha verification response is not received.", - Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } - try (InputStream in = entity.getContent()) { - JsonObject verificationResponse = new JsonParser().parse(IOUtils.toString(in)).getAsJsonObject(); - reCaptchaVerificationResponseDTO.setSuccess(verificationResponse.get( - CaptchaConstants.CAPTCHA_SUCCESS).getAsBoolean()); - } - } catch (IOException e) { - log.error("Unable to read the verification response.", e); - RecoveryUtil.handleBadRequest("Unable to read the verification response.", - Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); + if (entity == null) { + RecoveryUtil.handleBadRequest("ReCaptcha verification response is not received.", + Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); + } + try (InputStream in = entity.getContent()) { + JsonObject verificationResponse = new JsonParser().parse(IOUtils.toString(in)).getAsJsonObject(); + + if (CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE.equals(reCaptchaType)) { + // For Recaptcha Enterprise. + JsonObject tokenProperties = verificationResponse.get(CaptchaConstants.CAPTCHA_TOKEN_PROPERTIES) + .getAsJsonObject(); + boolean success = tokenProperties.get(CaptchaConstants.CAPTCHA_VALID).getAsBoolean(); + reCaptchaVerificationResponseDTO.setSuccess(success); + } else { + // For ReCaptcha v2 and v3. + reCaptchaVerificationResponseDTO.setSuccess(verificationResponse.get( + CaptchaConstants.CAPTCHA_SUCCESS).getAsBoolean()); } + } catch (IOException e) { + log.error("Unable to read the verification response.", e); + RecoveryUtil.handleBadRequest("Unable to read the verification response.", + Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); } return Response.ok(reCaptchaVerificationResponseDTO).build(); diff --git a/components/org.wso2.carbon.identity.api.user.recovery/src/test/java/org/wso2/carbon/identity/recovery/endpoint/impl/CaptchaApiServiceImplTest.java b/components/org.wso2.carbon.identity.api.user.recovery/src/test/java/org/wso2/carbon/identity/recovery/endpoint/impl/CaptchaApiServiceImplTest.java index 354792e128..c78f19b42e 100644 --- a/components/org.wso2.carbon.identity.api.user.recovery/src/test/java/org/wso2/carbon/identity/recovery/endpoint/impl/CaptchaApiServiceImplTest.java +++ b/components/org.wso2.carbon.identity.api.user.recovery/src/test/java/org/wso2/carbon/identity/recovery/endpoint/impl/CaptchaApiServiceImplTest.java @@ -109,6 +109,7 @@ public void testGetCaptcha(boolean reCaptchaEnabled, String reCaptchaType, } public Properties getSampleConfigFile() throws IOException { + Path path = Paths.get("src/test/resources", "repository", "conf", "identity", CaptchaConstants.CAPTCHA_CONFIG_FILE_NAME); Properties sampleProperties = new Properties(); diff --git a/components/org.wso2.carbon.identity.api.user.recovery/src/test/resources/repository/conf/identity/captcha-config.properties b/components/org.wso2.carbon.identity.api.user.recovery/src/test/resources/repository/conf/identity/captcha-config.properties index b920c175db..7980a19a37 100644 --- a/components/org.wso2.carbon.identity.api.user.recovery/src/test/resources/repository/conf/identity/captcha-config.properties +++ b/components/org.wso2.carbon.identity.api.user.recovery/src/test/resources/repository/conf/identity/captcha-config.properties @@ -21,9 +21,6 @@ # Enable Google reCAPTCHA recaptcha.enabled=true -# Google reCAPTCHA type -recaptcha.type=default - # reCaptcha API URL recaptcha.api.url=https://www.google.com/recaptcha/api.js diff --git a/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/util/CaptchaConstants.java b/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/util/CaptchaConstants.java index fd9f284922..742865a234 100644 --- a/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/util/CaptchaConstants.java +++ b/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/util/CaptchaConstants.java @@ -76,7 +76,7 @@ public class CaptchaConstants { public static final String CAPTCHA_TOKEN_PROPERTIES = "tokenProperties"; public static final String CAPTCHA_RISK_ANALYSIS = "riskAnalysis"; // Captcha Types. - public static final String RE_CAPTCHA_TYPE_ENTERPRISE = "enterprise"; + public static final String RE_CAPTCHA_TYPE_ENTERPRISE = "recaptcha-enterprise"; // Default value for threshold for score in reCAPTCHA v3. public static final double CAPTCHA_V3_DEFAULT_THRESHOLD = 0.5; diff --git a/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/util/CaptchaUtil.java b/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/util/CaptchaUtil.java index 875b1e9e6f..a7d4b69e7a 100644 --- a/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/util/CaptchaUtil.java +++ b/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/util/CaptchaUtil.java @@ -272,6 +272,46 @@ public static Map getClaimValues(User user, int tenantId, return claimValues; } + public static boolean isValidCaptcha(String reCaptchaResponse) throws CaptchaException { + + CloseableHttpClient httpclient = HttpClientBuilder.create().useSystemProperties().build(); + String reCaptchaType = CaptchaDataHolder.getInstance().getReCaptchaType(); + + HttpPost httpPost; + + // If the reCaptcha type is defined and, it is enterprise, the enterprise process will be done. Otherwise, + // the reCaptcha v2/v3 process will be done. + if (CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE.equals(reCaptchaType)) { + // For ReCaptcha Enterprise. + httpPost = createReCaptchaEnterpriseVerificationHttpPost(reCaptchaResponse); + } else { + // For ReCaptcha v2 and v3. + httpPost = createReCaptchaVerificationHttpPost(reCaptchaResponse); + } + + HttpResponse response; + try { + response = httpclient.execute(httpPost); + } catch (IOException e) { + throw new CaptchaServerException("Unable to get the verification response.", e); + } + + HttpEntity entity = response.getEntity(); + if (entity == null) { + throw new CaptchaServerException("reCaptcha verification response is not received."); + } + + if (CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE.equals(reCaptchaType)) { + // For ReCaptcha Enterprise. + verifyReCaptchaEnterpriseResponse(entity); + } else { + // For Recaptcha v2 and v3. + verifyReCaptchaResponse(entity); + } + + return true; + } + private static HttpPost createReCaptchaEnterpriseVerificationHttpPost(String reCaptchaResponse) { HttpPost httpPost; @@ -394,46 +434,6 @@ private static void verifyReCaptchaResponse(HttpEntity entity) } } - public static boolean isValidCaptcha(String reCaptchaResponse) throws CaptchaException { - - CloseableHttpClient httpclient = HttpClientBuilder.create().useSystemProperties().build(); - String reCaptchaType = CaptchaDataHolder.getInstance().getReCaptchaType(); - - HttpPost httpPost; - - // If the reCaptcha type is defined and, it is enterprise, the enterprise process will be done. Otherwise, - // the reCaptcha v2/v3 process will be done. - if (CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE.equals(reCaptchaType)) { - // For ReCaptcha Enterprise. - httpPost = createReCaptchaEnterpriseVerificationHttpPost(reCaptchaResponse); - } else { - // For ReCaptcha v2 and v3. - httpPost = createReCaptchaVerificationHttpPost(reCaptchaResponse); - } - - HttpResponse response; - try { - response = httpclient.execute(httpPost); - } catch (IOException e) { - throw new CaptchaServerException("Unable to get the verification response.", e); - } - - HttpEntity entity = response.getEntity(); - if (entity == null) { - throw new CaptchaServerException("reCaptcha verification response is not received."); - } - - if (CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE.equals(reCaptchaType)) { - // For ReCaptcha Enterprise. - verifyReCaptchaEnterpriseResponse(entity); - } else { - // For Recaptcha v2 and v3. - verifyReCaptchaResponse(entity); - } - - return true; - } - public static boolean isMaximumFailedLoginAttemptsReached(String usernameWithDomain, String tenantDomain) throws CaptchaException { @@ -578,7 +578,7 @@ private static void setReCaptchaConfigs(Properties properties) { } CaptchaDataHolder.getInstance().setReCaptchaAPIKey(reCaptchaAPIKey); } else { - // Secret Key is only required if recaptcha enterprise is not enabled + // Secret Key is only required if recaptcha enterprise is not enabled. String reCaptchaSecretKey = properties.getProperty(CaptchaConstants.RE_CAPTCHA_SECRET_KEY); if (StringUtils.isBlank(reCaptchaSecretKey)) { throw new RuntimeException(getValidationErrorMessage(CaptchaConstants.RE_CAPTCHA_SECRET_KEY)); diff --git a/components/org.wso2.carbon.identity.captcha/src/test/java/org/wso2/carbon/identity/captcha/util/CaptchaUtilTest.java b/components/org.wso2.carbon.identity.captcha/src/test/java/org/wso2/carbon/identity/captcha/util/CaptchaUtilTest.java index 5d6ee46624..0a8083d3c4 100644 --- a/components/org.wso2.carbon.identity.captcha/src/test/java/org/wso2/carbon/identity/captcha/util/CaptchaUtilTest.java +++ b/components/org.wso2.carbon.identity.captcha/src/test/java/org/wso2/carbon/identity/captcha/util/CaptchaUtilTest.java @@ -22,7 +22,6 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.testng.Assert; -import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.wso2.carbon.identity.captcha.internal.CaptchaDataHolder; @@ -32,7 +31,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import static org.testng.Assert.*; + +import static org.testng.Assert.assertThrows; /** * Unit tests for CaptchaUtil.java @@ -109,8 +109,6 @@ public void testCreateReCaptchaEnterpriseVerificationHttpPost() throws NoSuchMet CaptchaDataHolder.getInstance().setReCaptchaSiteKey("dummySiteKey"); CaptchaDataHolder.getInstance().setReCaptchaProjectID("dummyProjectId"); - - Method method = getCreateReCaptchaEnterpriseVerificationHttpPostMethod(); HttpPost httpPost = (HttpPost) method.invoke(null, "reCaptchaEnterpriseResponse"); String expectedURI = RECAPTCHA_ENTERPRISE_API_URL+ "/v1/projects/dummyProjectId/assessments?key=dummyKey"; @@ -145,7 +143,7 @@ public void testVerifyReCaptchaEnterpriseResponseWithHighScore() throws IOExcept HttpEntity httpEntity = Mockito.mock(HttpEntity.class); Mockito.when(httpEntity.getContent()).thenReturn(new ByteArrayInputStream( verificationResponse.toString().getBytes())); - // verify no exception is thrown for high score + // Verify no exception is thrown for high score. method.invoke(null, httpEntity); } @@ -160,7 +158,7 @@ public void testVerifyReCaptchaEnterpriseResponseWithLowScore() throws IOExcepti HttpEntity httpEntity = Mockito.mock(HttpEntity.class); Mockito.when(httpEntity.getContent()).thenReturn(new ByteArrayInputStream(verificationResponse.toString(). getBytes())); - // verify an exception is thrown for low score + // Verify an exception is thrown for low score. assertThrows(InvocationTargetException.class, () -> method.invoke(null, httpEntity)); } @@ -175,7 +173,7 @@ public void testVerifyReCaptchaEnterpriseResponseWithInvalidResponse() throws IO HttpEntity httpEntity = Mockito.mock(HttpEntity.class); Mockito.when(httpEntity.getContent()).thenReturn(new ByteArrayInputStream(verificationResponse. toString().getBytes())); - // verify an exception is thrown for invalid response + // Verify an exception is thrown for invalid response. assertThrows(InvocationTargetException.class, () -> method.invoke(null, httpEntity)); } @@ -191,7 +189,7 @@ public void testVerifyReCaptchaResponseWithHighScore() throws IOException, NoSuc HttpEntity httpEntity = Mockito.mock(HttpEntity.class); Mockito.when(httpEntity.getContent()).thenReturn(new ByteArrayInputStream(verificationResponse.toString(). getBytes())); - // verify no exception is thrown for high score + // Verify no exception is thrown for high score. method.invoke(null, httpEntity); } @@ -206,7 +204,7 @@ public void testVerifyReCaptchaResponseWithLowScore() throws IOException, NoSuch HttpEntity httpEntity = Mockito.mock(HttpEntity.class); Mockito.when(httpEntity.getContent()).thenReturn(new ByteArrayInputStream(verificationResponse.toString(). getBytes())); - // verify no exception is thrown for low score + // Verify no exception is thrown for low score. assertThrows(InvocationTargetException.class, () -> method.invoke(null, httpEntity)); } @@ -221,7 +219,7 @@ public void testVerifyReCaptchaResponseWithInvalidResponse() throws IOException, HttpEntity httpEntity = Mockito.mock(HttpEntity.class); Mockito.when(httpEntity.getContent()).thenReturn(new ByteArrayInputStream(verificationResponse.toString(). getBytes())); - // verify no exception is thrown for invalid response + // Verify no exception is thrown for invalid response. assertThrows(InvocationTargetException.class, () -> method.invoke(null, httpEntity)); } }