From 588fd1859d9b00d5e1bd19f57d0cab0fe94b28c6 Mon Sep 17 00:00:00 2001 From: AwesomeNipun Date: Mon, 5 Jun 2023 10:10:41 +0530 Subject: [PATCH] Change code logic using reCaptcha type --- .../endpoint/dto/ReCaptchaPropertiesDTO.java | 26 +- .../recovery/endpoint/Utils/RecoveryUtil.java | 95 +++--- .../endpoint/impl/CaptchaApiServiceImpl.java | 51 ++-- .../main/resources/api.identity.recovery.yaml | 4 +- .../conf/identity/captcha-config.properties | 3 + .../captcha/internal/CaptchaDataHolder.java | 10 +- .../captcha/util/CaptchaConstants.java | 8 +- .../identity/captcha/util/CaptchaUtil.java | 272 ++++++++++-------- .../conf/captcha-config.properties.j2 | 4 +- 9 files changed, 239 insertions(+), 234 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.user.recovery/src/gen/java/org/wso2/carbon/identity/recovery/endpoint/dto/ReCaptchaPropertiesDTO.java b/components/org.wso2.carbon.identity.api.user.recovery/src/gen/java/org/wso2/carbon/identity/recovery/endpoint/dto/ReCaptchaPropertiesDTO.java index 36c9599445..40543abfe5 100644 --- a/components/org.wso2.carbon.identity.api.user.recovery/src/gen/java/org/wso2/carbon/identity/recovery/endpoint/dto/ReCaptchaPropertiesDTO.java +++ b/components/org.wso2.carbon.identity.api.user.recovery/src/gen/java/org/wso2/carbon/identity/recovery/endpoint/dto/ReCaptchaPropertiesDTO.java @@ -10,21 +10,21 @@ @ApiModel(description = "") public class ReCaptchaPropertiesDTO { - - - + + + private Boolean reCaptchaEnabled = null; - private Boolean reCaptchaEnterpriseEnabled = null; + private String reCaptchaType = null; private String reCaptchaKey = null; - - + + private String reCaptchaAPI = null; - + /** **/ @ApiModelProperty(value = "") @@ -40,12 +40,12 @@ public void setReCaptchaEnabled(Boolean reCaptchaEnabled) { /** **/ @ApiModelProperty(value = "") - @JsonProperty("reCaptchaEnterpriseEnabled") - public Boolean getReCaptchaEnterpriseEnabled() { - return reCaptchaEnterpriseEnabled; + @JsonProperty("reCaptchaType") + public String getReCaptchaType() { + return reCaptchaType; } - public void setReCaptchaEnterpriseEnabled(Boolean reCaptchaEnterpriseEnabled) { - this.reCaptchaEnterpriseEnabled = reCaptchaEnterpriseEnabled; + public void setReCaptchaType(String reCaptchaType) { + this.reCaptchaType = reCaptchaType; } @@ -80,7 +80,7 @@ public String toString() { sb.append("class ReCaptchaPropertiesDTO {\n"); sb.append(" reCaptchaEnabled: ").append(reCaptchaEnabled).append("\n"); - sb.append(" reCaptchaEnterpriseEnabled: ").append(reCaptchaEnterpriseEnabled).append("\n"); + sb.append(" reCaptchaEnterpriseEnabled: ").append(reCaptchaType).append("\n"); sb.append(" reCaptchaKey: ").append(reCaptchaKey).append("\n"); sb.append(" reCaptchaAPI: ").append(reCaptchaAPI).append("\n"); sb.append("}\n"); 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 a6f34da27d..dc20efe75c 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 @@ -6,7 +6,6 @@ import org.apache.http.HttpResponse; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; @@ -453,32 +452,28 @@ public static Properties getValidatedCaptchaConfigs() { private static Properties validateCaptchaConfigs(Properties properties) { boolean reCaptchaEnabled = Boolean.valueOf(properties.getProperty(CaptchaConstants.RE_CAPTCHA_ENABLED)); + String reCaptchaType = properties.getProperty(CaptchaConstants.RE_CAPTCHA_TYPE); - boolean reCaptchaEnterpriseEnabled = Boolean.valueOf(properties.getProperty(CaptchaConstants - .RE_CAPTCHA_ENTERPRISE_ENABLED)); - - if (reCaptchaEnabled) { - if (StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_SITE_KEY))) { - RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants.RE_CAPTCHA_SITE_KEY), - Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } - if (StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_API_URL))) { - RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants.RE_CAPTCHA_API_URL), - Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } - if (StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_SECRET_KEY))) { - RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants.RE_CAPTCHA_SECRET_KEY), - Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } - if (StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_VERIFY_URL))) { - RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants.RE_CAPTCHA_VERIFY_URL), - Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } - if (reCaptchaEnterpriseEnabled && - StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_PROJECT_ID))) { - RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants - .RE_CAPTCHA_PROJECT_ID), Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } + if (reCaptchaEnabled && StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_SITE_KEY))) { + RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants.RE_CAPTCHA_SITE_KEY), + Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); + } + if (StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_API_URL))) { + RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants.RE_CAPTCHA_API_URL), + Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); + } + if (reCaptchaEnabled && StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_SECRET_KEY))) { + RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants.RE_CAPTCHA_SECRET_KEY), + Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); + } + if (StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_VERIFY_URL))) { + RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants.RE_CAPTCHA_VERIFY_URL), + Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); + } + if (!StringUtils.isBlank(reCaptchaType) && reCaptchaType.equals(CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE) && + StringUtils.isBlank(properties.getProperty(CaptchaConstants.RE_CAPTCHA_PROJECT_ID))) { + RecoveryUtil.handleBadRequest(String.format("%s is not found ", CaptchaConstants + .RE_CAPTCHA_PROJECT_ID), Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); } return properties; } @@ -494,45 +489,19 @@ public static HttpResponse makeCaptchaVerificationHttpRequest(ReCaptchaResponseT Properties properties) { HttpResponse response = null; - String reCaptchaVerifyUrl = properties.getProperty(CaptchaConstants.RE_CAPTCHA_VERIFY_URL); String reCaptchaSecretKey = properties.getProperty(CaptchaConstants.RE_CAPTCHA_SECRET_KEY); - boolean reCaptchaEnterpriseEnabled = - Boolean.valueOf(properties.getProperty(CaptchaConstants.RE_CAPTCHA_ENTERPRISE_ENABLED)); + String reCaptchaVerifyUrl = properties.getProperty(CaptchaConstants.RE_CAPTCHA_VERIFY_URL); CloseableHttpClient httpclient = HttpClientBuilder.create().useSystemProperties().build(); - HttpPost httpPost; - - if (reCaptchaEnterpriseEnabled) { - // For ReCaptcha Enterprise. - String projectID = properties.getProperty(CaptchaConstants.RE_CAPTCHA_PROJECT_ID); - String siteKey = properties.getProperty(CaptchaConstants.RE_CAPTCHA_SITE_KEY); - String verifyUrl = reCaptchaVerifyUrl + "/v1/projects/" + projectID + - "/assessments?key=" + reCaptchaSecretKey; - httpPost = new HttpPost(verifyUrl); - httpPost.setHeader(Constants.HEADER_CONTENT_TYPE, Constants.APPLICATION_JSON); - String json = String.format("{ \"event\": { \"token\": \"%s\", \"siteKey\": \"%s\" } }", reCaptchaResponse, - siteKey); - StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8); - httpPost.setEntity(entity); - - try { - response = httpclient.execute(httpPost); - } catch (IOException e) { - RecoveryUtil.handleBadRequest(String.format("Unable to get the verification response : %s", - e.getMessage()), Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } - } else { - // For ReCaptcha v2 and v3. - httpPost = new HttpPost(reCaptchaVerifyUrl); - List params = Arrays.asList(new BasicNameValuePair("secret", reCaptchaSecretKey), - new BasicNameValuePair("response", reCaptchaResponse.getToken())); - httpPost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8)); - - try { - response = httpclient.execute(httpPost); - } catch (IOException e) { - RecoveryUtil.handleBadRequest(String.format("Unable to get the verification response : %s", - e.getMessage()), Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } + HttpPost httppost = new HttpPost(reCaptchaVerifyUrl); + List params = Arrays.asList(new BasicNameValuePair("secret", reCaptchaSecretKey), + new BasicNameValuePair("response", reCaptchaResponse.getToken())); + httppost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8)); + + try { + response = httpclient.execute(httppost); + } catch (IOException e) { + RecoveryUtil.handleBadRequest(String.format("Unable to get the verification response : %s", e.getMessage()), + Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); } return response; } 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 d04b087d8a..52337bb508 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,6 +20,7 @@ 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; @@ -42,8 +43,6 @@ */ public class CaptchaApiServiceImpl extends CaptchaApiService { - private static final String SUCCESS = "success"; - private static final String VALID = "valid"; private static final Log log = LogFactory.getLog(CaptchaApiServiceImpl.class); private final String RECAPTCHA = "ReCaptcha"; @@ -56,22 +55,22 @@ public Response getCaptcha(String captchaType, String recoveryType, String tenan Properties properties = RecoveryUtil.getValidatedCaptchaConfigs(); boolean reCaptchaEnabled = Boolean.valueOf(properties.getProperty(CaptchaConstants.RE_CAPTCHA_ENABLED)); - boolean reCaptchaEnterpriseEnabled = - Boolean.valueOf(properties.getProperty(CaptchaConstants.RE_CAPTCHA_ENTERPRISE_ENABLED)); + String reCaptchaType = properties.getProperty(CaptchaConstants.RE_CAPTCHA_TYPE); boolean forcefullyEnabledRecaptchaForAllTenants = Boolean.valueOf(properties.getProperty(CaptchaConstants.FORCEFULLY_ENABLED_RECAPTCHA_FOR_ALL_TENANTS)); ReCaptchaPropertiesDTO reCaptchaPropertiesDTO = new ReCaptchaPropertiesDTO(); if (reCaptchaEnabled && (forcefullyEnabledRecaptchaForAllTenants || RecoveryUtil.checkCaptchaEnabledResidentIdpConfiguration(tenantDomain, recoveryType))) { - reCaptchaPropertiesDTO.setReCaptchaEnabled(reCaptchaEnabled); + reCaptchaPropertiesDTO.setReCaptchaEnabled(true); reCaptchaPropertiesDTO.setReCaptchaKey(properties.getProperty(CaptchaConstants.RE_CAPTCHA_SITE_KEY)); reCaptchaPropertiesDTO.setReCaptchaAPI(properties.getProperty(CaptchaConstants.RE_CAPTCHA_API_URL)); - reCaptchaPropertiesDTO.setReCaptchaEnterpriseEnabled(reCaptchaEnterpriseEnabled); + if (!StringUtils.isBlank(reCaptchaType)) { + reCaptchaPropertiesDTO.setReCaptchaType(reCaptchaType); + } return Response.ok(reCaptchaPropertiesDTO).build(); } else { reCaptchaPropertiesDTO.setReCaptchaEnabled(false); - reCaptchaPropertiesDTO.setReCaptchaEnterpriseEnabled(false); return Response.ok(reCaptchaPropertiesDTO).build(); } } @@ -85,8 +84,7 @@ public Response verifyCaptcha(ReCaptchaResponseTokenDTO reCaptchaResponse, Strin Properties properties = RecoveryUtil.getValidatedCaptchaConfigs(); boolean reCaptchaEnabled = Boolean.valueOf(properties.getProperty(CaptchaConstants.RE_CAPTCHA_ENABLED)); - boolean reCaptchaEnterpriseEnabled = - Boolean.valueOf(properties.getProperty(CaptchaConstants.RE_CAPTCHA_ENTERPRISE_ENABLED)); + String reCaptchaType = properties.getProperty(CaptchaConstants.RE_CAPTCHA_TYPE); if (!reCaptchaEnabled) { RecoveryUtil.handleBadRequest("ReCaptcha is disabled", Constants.INVALID); @@ -96,34 +94,37 @@ public Response verifyCaptcha(ReCaptchaResponseTokenDTO reCaptchaResponse, Strin HttpEntity entity = response.getEntity(); ReCaptchaVerificationResponseDTO reCaptchaVerificationResponseDTO = new ReCaptchaVerificationResponseDTO(); - if (reCaptchaEnterpriseEnabled) { - // For ReCaptcha Enterprise. - if (entity == null) { - RecoveryUtil.handleBadRequest("ReCaptcha Enterprise verification response is not received.", - Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); - } + if (StringUtils.isBlank(reCaptchaType) || reCaptchaType.equals(CaptchaConstants. + RE_CAPTCHA_TYPE_DEFAULT)){ + // 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(); - JsonObject tokenProperties = verificationResponse.get("tokenProperties").getAsJsonObject(); - boolean success = tokenProperties.get(VALID).getAsBoolean(); - reCaptchaVerificationResponseDTO.setSuccess(success); + 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); } - } else { - // For ReCaptcha v2 and v3. + } else if (reCaptchaType.equals(CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE)) { + // For ReCaptcha Enterprise. + if (entity == null) { + RecoveryUtil.handleBadRequest("ReCaptcha Enterprise verification response is not received.", + Constants.STATUS_INTERNAL_SERVER_ERROR_MESSAGE_DEFAULT); + } 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(SUCCESS).getAsBoolean()); + 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); diff --git a/components/org.wso2.carbon.identity.api.user.recovery/src/main/resources/api.identity.recovery.yaml b/components/org.wso2.carbon.identity.api.user.recovery/src/main/resources/api.identity.recovery.yaml index 1713ce4466..770c4c24d8 100644 --- a/components/org.wso2.carbon.identity.api.user.recovery/src/main/resources/api.identity.recovery.yaml +++ b/components/org.wso2.carbon.identity.api.user.recovery/src/main/resources/api.identity.recovery.yaml @@ -790,8 +790,8 @@ definitions: properties: reCaptchaEnabled: type: boolean - reCaptchaEnterpriseEnabled: - type: boolean + reCaptchaType: + type: string reCaptchaKey: type: string reCaptchaAPI: 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 7980a19a37..b920c175db 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,6 +21,9 @@ # 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/internal/CaptchaDataHolder.java b/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/internal/CaptchaDataHolder.java index f55f563301..260bb81417 100644 --- a/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/internal/CaptchaDataHolder.java +++ b/components/org.wso2.carbon.identity.captcha/src/main/java/org/wso2/carbon/identity/captcha/internal/CaptchaDataHolder.java @@ -37,7 +37,7 @@ public class CaptchaDataHolder { private boolean reCaptchaEnabled; - private boolean reCaptchaEnterpriseEnabled; + private String reCaptchaType; private String reCaptchaAPIUrl; @@ -88,14 +88,14 @@ public void setReCaptchaEnabled(boolean reCaptchaEnabled) { this.reCaptchaEnabled = reCaptchaEnabled; } - public boolean isReCaptchaEnterpriseEnabled() { + public String getReCaptchaType() { - return reCaptchaEnterpriseEnabled; + return reCaptchaType; } - public void setReCaptchaEnterpriseEnabled(boolean reCaptchaEnterpriseEnabled) { + public void setReCaptchaType(String reCaptchaType) { - this.reCaptchaEnterpriseEnabled = reCaptchaEnterpriseEnabled; + this.reCaptchaType = reCaptchaType; } public String getReCaptchaProjectID() { 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 2e1a2fbaa9..50848fdddc 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 @@ -34,7 +34,7 @@ public class CaptchaConstants { public static final String RE_CAPTCHA_ENABLED = "recaptcha.enabled"; - public static final String RE_CAPTCHA_ENTERPRISE_ENABLED = "recaptcha.enterprise.enabled"; + public static final String RE_CAPTCHA_TYPE = "recaptcha.type"; public static final String FORCEFULLY_ENABLED_RECAPTCHA_FOR_ALL_TENANTS = "recaptcha" + ".forcefullyEnabledForAllTenants"; @@ -70,8 +70,12 @@ public class CaptchaConstants { public static final String AUTH_FAILURE_MSG = "authFailureMsg"; public static final String RECAPTCHA_FAIL_MSG_KEY = "recaptcha.fail.message"; public static final String TRUE = "true"; - public static final String CAPTCHA_VALID = "valid"; + 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_DEFAULT = "default"; + public static final String RE_CAPTCHA_TYPE_ENTERPRISE = "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 378177865a..9b1bdeeb40 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 @@ -93,7 +93,7 @@ public class CaptchaUtil { private static final Log log = LogFactory.getLog(CaptchaUtil.class); - public static void buildReCaptchaFilterProperties() throws CaptchaServerException { + public static void buildReCaptchaFilterProperties() { Path path = Paths.get(getCarbonHomeDirectory().toString(), "repository", "conf", "identity", CaptchaConstants.CAPTCHA_CONFIG_FILE_NAME); @@ -109,8 +109,6 @@ public static void buildReCaptchaFilterProperties() throws CaptchaServerExceptio boolean reCaptchaEnabled = Boolean.parseBoolean(properties.getProperty(CaptchaConstants .RE_CAPTCHA_ENABLED)); - boolean reCaptchaEnterpriseEnabled = Boolean.parseBoolean(properties.getProperty(CaptchaConstants - .RE_CAPTCHA_ENTERPRISE_ENABLED)); String reCaptchaFailedRedirectUrls = properties.getProperty(CaptchaConstants. RE_CAPTCHA_FAILED_REDIRECT_URLS); @@ -118,26 +116,17 @@ public static void buildReCaptchaFilterProperties() throws CaptchaServerExceptio CaptchaDataHolder.getInstance().setReCaptchaErrorRedirectUrls(reCaptchaFailedRedirectUrls); } - if (!reCaptchaEnabled && reCaptchaEnterpriseEnabled) { - throw new CaptchaServerException("reCaptcha is not enabled. Please enable reCaptcha to use " + - "reCaptcha Enterprise."); - } - if (reCaptchaEnabled) { CaptchaDataHolder.getInstance().setReCaptchaEnabled(true); - if (reCaptchaEnterpriseEnabled) { - CaptchaDataHolder.getInstance().setReCaptchaEnterpriseEnabled(true); - } else { - CaptchaDataHolder.getInstance().setReCaptchaEnterpriseEnabled(false); - } resolveSecrets(properties); setReCaptchaConfigs(properties); //setSSOLoginConnectorConfigs(properties); //setPathBasedConnectorConfigs(properties); } else { CaptchaDataHolder.getInstance().setReCaptchaEnabled(false); - CaptchaDataHolder.getInstance().setReCaptchaEnterpriseEnabled(false); + } + } } @@ -283,136 +272,163 @@ public static Map getClaimValues(User user, int tenantId, return claimValues; } - public static boolean isValidCaptcha(String reCaptchaResponse) throws CaptchaException { - - CloseableHttpClient httpclient = HttpClientBuilder.create().useSystemProperties().build(); - final double scoreThreshold = CaptchaDataHolder.getInstance().getReCaptchaScoreThreshold(); - boolean isRecaptchaEnterpriseEnabled = CaptchaDataHolder.getInstance().isReCaptchaEnterpriseEnabled(); + private static HttpPost createReCaptchaEnterpriseVerificationHttpPost(String reCaptchaResponse) { HttpPost httpPost; - if (isRecaptchaEnterpriseEnabled) { - // For ReCaptcha Enterprise. - String recaptchaUrl = CaptchaDataHolder.getInstance().getReCaptchaVerifyUrl(); - String projectID = CaptchaDataHolder.getInstance().getReCaptchaProjectID(); - String siteKey = CaptchaDataHolder.getInstance().getReCaptchaSiteKey(); - String secretKey = CaptchaDataHolder.getInstance().getReCaptchaSecretKey(); + String recaptchaUrl = CaptchaDataHolder.getInstance().getReCaptchaVerifyUrl(); + String projectID = CaptchaDataHolder.getInstance().getReCaptchaProjectID(); + String siteKey = CaptchaDataHolder.getInstance().getReCaptchaSiteKey(); + String secretKey = CaptchaDataHolder.getInstance().getReCaptchaSecretKey(); - String verifyUrl = recaptchaUrl + "/v1/projects/" + projectID + "/assessments?key=" + secretKey; - httpPost = new HttpPost(verifyUrl); + String verifyUrl = recaptchaUrl + "/v1/projects/" + projectID + "/assessments?key=" + secretKey; + httpPost = new HttpPost(verifyUrl); - httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); + httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); - String json = String.format("{ \"event\": { \"token\": \"%s\", \"siteKey\": \"%s\" } }", reCaptchaResponse, - siteKey); + String json = String.format("{ \"event\": { \"token\": \"%s\", \"siteKey\": \"%s\" } }", reCaptchaResponse, + siteKey); - StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8); + StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8); - httpPost.setEntity(entity); + httpPost.setEntity(entity); - HttpResponse response; - try { - response = httpclient.execute(httpPost); - } catch (IOException e) { - throw new CaptchaServerException("Unable to get the verification response."); - } + return httpPost; + } - HttpEntity responseEntity = response.getEntity(); + private static HttpPost createReCaptchaVerificationHttpPost(String reCaptchaResponse) { - if (responseEntity == null) { - throw new CaptchaServerException("reCaptcha verification response is not received."); - } + HttpPost httpPost; + httpPost = new HttpPost(CaptchaDataHolder.getInstance().getReCaptchaVerifyUrl()); + List params = Arrays.asList(new BasicNameValuePair("secret", CaptchaDataHolder + .getInstance().getReCaptchaSecretKey()), + new BasicNameValuePair("response", reCaptchaResponse)); + httpPost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8)); - try { - try (InputStream in = responseEntity.getContent()) { - JsonElement jsonElement = JsonParser.parseReader(new InputStreamReader(in, StandardCharsets.UTF_8)); - JsonObject verificationResponse = jsonElement.getAsJsonObject(); - if (verificationResponse == null) { - throw new CaptchaClientException("Error receiving reCaptcha response from the server"); - } - JsonObject tokenProperties = verificationResponse.get("tokenProperties").getAsJsonObject(); - boolean success = tokenProperties.get(CaptchaConstants.CAPTCHA_VALID).getAsBoolean(); + return httpPost; + } + + private static void verifyReCaptchaEnterpriseResponse(HttpEntity entity) + throws CaptchaServerException, CaptchaClientException { + + final double scoreThreshold = CaptchaDataHolder.getInstance().getReCaptchaScoreThreshold(); - JsonObject riskAnalysis = verificationResponse.get("riskAnalysis").getAsJsonObject(); + try { + try (InputStream in = entity.getContent()) { + JsonElement jsonElement = JsonParser.parseReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + JsonObject verificationResponse = jsonElement.getAsJsonObject(); + if (verificationResponse == null) { + throw new CaptchaClientException("Error receiving reCaptcha response from the server"); + } + JsonObject tokenProperties = verificationResponse.get(CaptchaConstants.CAPTCHA_TOKEN_PROPERTIES). + getAsJsonObject(); + boolean success = tokenProperties.get(CaptchaConstants.CAPTCHA_VALID).getAsBoolean(); - // Whether this request was a valid reCAPTCHA token. - if (!success) { - throw new CaptchaClientException("reCaptcha token is invalid. Error:" + - verificationResponse.get("error-codes")); + JsonObject riskAnalysis = verificationResponse.get(CaptchaConstants.CAPTCHA_RISK_ANALYSIS). + getAsJsonObject(); + + // Whether this request was a valid reCAPTCHA token. + if (!success) { + throw new CaptchaClientException("reCaptcha token is invalid. Error:" + + verificationResponse.get("error-codes")); + } + if (riskAnalysis.get(CaptchaConstants.CAPTCHA_SCORE) != null) { + double score = riskAnalysis.get(CaptchaConstants.CAPTCHA_SCORE).getAsDouble(); + // reCAPTCHA enterprise response contains score. + if (log.isDebugEnabled()) { + log.debug("reCAPTCHA Enterprise response { timestamp:" + + tokenProperties.get("createTime") + ", action: " + + tokenProperties.get("action") + ", score: " + score + " }"); } - if (riskAnalysis.get(CaptchaConstants.CAPTCHA_SCORE) != null) { - double score = riskAnalysis.get(CaptchaConstants.CAPTCHA_SCORE).getAsDouble(); - // reCAPTCHA enterprise response contains score. - if (log.isDebugEnabled()) { - log.debug("reCAPTCHA Enterprise response { timestamp:" + - tokenProperties.get("createTime") + ", action: " + - tokenProperties.get("action") + ", score: " + score + " }"); - } - if (score < scoreThreshold) { - throw new CaptchaClientException("reCaptcha score is less than the threshold."); - } + if (score < scoreThreshold) { + throw new CaptchaClientException("reCaptcha score is less than the threshold."); } } - } catch (IOException e) { - throw new CaptchaServerException("Unable to read the verification response.", e); - } catch (ClassCastException e) { - throw new CaptchaServerException("Unable to cast the response value.", e); - } - } else { - // For Recaptcha v2 and v3. - httpPost = new HttpPost(CaptchaDataHolder.getInstance().getReCaptchaVerifyUrl()); - List params = Arrays.asList(new BasicNameValuePair("secret", CaptchaDataHolder - .getInstance().getReCaptchaSecretKey()), - new BasicNameValuePair("response", reCaptchaResponse)); - httpPost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8)); - - HttpResponse response; - try { - response = httpclient.execute(httpPost); - } catch (IOException e) { - throw new CaptchaServerException("Unable to get the verification response.", e); } + } catch (IOException e) { + throw new CaptchaServerException("Unable to read the verification response.", e); + } catch (ClassCastException e) { + throw new CaptchaServerException("Unable to cast the response value.", e); + } + } - HttpEntity entity = response.getEntity(); - if (entity == null) { - throw new CaptchaServerException("reCaptcha verification response is not received."); - } + private static void verifyReCaptchaResponse(HttpEntity entity) + throws CaptchaServerException, CaptchaClientException { - try { - try (InputStream in = entity.getContent()) { - JsonElement jsonElement = JsonParser.parseReader(new InputStreamReader(in, StandardCharsets.UTF_8)); - JsonObject verificationResponse = jsonElement.getAsJsonObject(); - if (verificationResponse == null) { - throw new CaptchaClientException("Error receiving reCaptcha response from the server"); + final double scoreThreshold = CaptchaDataHolder.getInstance().getReCaptchaScoreThreshold(); + + try { + try (InputStream in = entity.getContent()) { + JsonElement jsonElement = JsonParser.parseReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + JsonObject verificationResponse = jsonElement.getAsJsonObject(); + if (verificationResponse == null) { + throw new CaptchaClientException("Error receiving reCaptcha response from the server"); + } + boolean success = verificationResponse.get(CaptchaConstants.CAPTCHA_SUCCESS).getAsBoolean(); + // Whether this request was a valid reCAPTCHA token. + if (!success) { + throw new CaptchaClientException("reCaptcha token is invalid. Error:" + + verificationResponse.get("error-codes")); + } + if (verificationResponse.get(CaptchaConstants.CAPTCHA_SCORE) != null) { + double score = verificationResponse.get(CaptchaConstants.CAPTCHA_SCORE).getAsDouble(); + // reCAPTCHA v3 response contains score. + if (log.isDebugEnabled()) { + log.debug("reCAPTCHA v3 response { timestamp:" + + verificationResponse.get("challenge_ts") + ", action: " + + verificationResponse.get("action") + ", score: " + score + " }"); } - boolean success = verificationResponse.get(CaptchaConstants.CAPTCHA_SUCCESS).getAsBoolean(); - // Whether this request was a valid reCAPTCHA token. - if (!success) { - throw new CaptchaClientException("reCaptcha token is invalid. Error:" + - verificationResponse.get("error-codes")); + if (score < scoreThreshold) { + throw new CaptchaClientException("reCaptcha score is less than the threshold."); } - if (verificationResponse.get(CaptchaConstants.CAPTCHA_SCORE) != null) { - double score = verificationResponse.get(CaptchaConstants.CAPTCHA_SCORE).getAsDouble(); - // reCAPTCHA v3 response contains score. - if (log.isDebugEnabled()) { - log.debug("reCAPTCHA v3 response { timestamp:" + - verificationResponse.get("challenge_ts") + ", action: " + - verificationResponse.get("action") + ", score: " + score + " }"); - } - if (score < scoreThreshold) { - throw new CaptchaClientException("reCaptcha score is less than the threshold."); - } - } else { - if (log.isDebugEnabled()) { - log.debug("reCAPTCHA v2 response { timestamp:" + - verificationResponse.get("challenge_ts") + " }"); - } + } else { + if (log.isDebugEnabled()) { + log.debug("reCAPTCHA v2 response { timestamp:" + + verificationResponse.get("challenge_ts") + " }"); } } - } catch (IOException e) { - throw new CaptchaServerException("Unable to read the verification response.", e); - } catch (ClassCastException e) { - throw new CaptchaServerException("Unable to cast the response value.", e); } + } catch (IOException e) { + throw new CaptchaServerException("Unable to read the verification response.", e); + } catch (ClassCastException e) { + throw new CaptchaServerException("Unable to cast the response value.", e); + } + } + + public static boolean isValidCaptcha(String reCaptchaResponse) throws CaptchaException { + + CloseableHttpClient httpclient = HttpClientBuilder.create().useSystemProperties().build(); + String reCaptchaType = CaptchaDataHolder.getInstance().getReCaptchaType(); + + HttpPost httpPost = null; + + if (StringUtils.isBlank(reCaptchaType) || reCaptchaType.equals(CaptchaConstants.RE_CAPTCHA_TYPE_DEFAULT)) { + // For ReCaptcha v2 and v3. + httpPost = createReCaptchaVerificationHttpPost(reCaptchaResponse); + } else if (reCaptchaType.equals(CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE)) { + // For ReCaptcha Enterprise. + httpPost = createReCaptchaEnterpriseVerificationHttpPost(reCaptchaResponse); + + } + + HttpResponse response; + try { + response = httpclient.execute(httpPost); + } catch (IOException e) { + throw new CaptchaServerException("Unable to get the verification response."); + } + + HttpEntity responseEntity = response.getEntity(); + + if (responseEntity == null) { + throw new CaptchaServerException("reCaptcha verification response is not received."); + } + + if (StringUtils.isBlank(reCaptchaType) || reCaptchaType.equals(CaptchaConstants.RE_CAPTCHA_TYPE_DEFAULT)) { + // For Recaptcha v2 and v3. + verifyReCaptchaResponse(responseEntity); + } else if (reCaptchaType.equals(CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE)) { + // For ReCaptcha Enterprise. + verifyReCaptchaEnterpriseResponse(responseEntity); } return true; } @@ -542,9 +558,12 @@ private static UserStoreManager getUserStoreManagerForUser(String userName, private static void setReCaptchaConfigs(Properties properties) { - boolean recaptchaEnterpriseEnabled = CaptchaDataHolder.getInstance().isReCaptchaEnterpriseEnabled(); + String reCaptchaType = properties.getProperty(CaptchaConstants.RE_CAPTCHA_TYPE); + if (!StringUtils.isBlank(reCaptchaType)) { + CaptchaDataHolder.getInstance().setReCaptchaType(reCaptchaType); + } - if (recaptchaEnterpriseEnabled){ + if (reCaptchaType.equals(CaptchaConstants.RE_CAPTCHA_TYPE_ENTERPRISE)) { // ReCaptcha Enterprise require Project ID. String reCaptchaProjectID = properties.getProperty(CaptchaConstants.RE_CAPTCHA_PROJECT_ID); if (StringUtils.isBlank(reCaptchaProjectID)) { @@ -789,6 +808,15 @@ public static Boolean isReCaptchaEnabled() { return CaptchaDataHolder.getInstance().isReCaptchaEnabled(); } + /** + * Get the ReCaptcha Type. + * @return ReCaptcha Type as a String. + */ + public static String reCaptchaType() { + + return CaptchaDataHolder.getInstance().getReCaptchaType(); + } + /** * Check whether ReCaptcha is enabled for the given flow. * diff --git a/features/org.wso2.carbon.identity.captcha.server.feature/resources/conf/captcha-config.properties.j2 b/features/org.wso2.carbon.identity.captcha.server.feature/resources/conf/captcha-config.properties.j2 index 88ec472a13..bd014187fa 100644 --- a/features/org.wso2.carbon.identity.captcha.server.feature/resources/conf/captcha-config.properties.j2 +++ b/features/org.wso2.carbon.identity.captcha.server.feature/resources/conf/captcha-config.properties.j2 @@ -21,8 +21,8 @@ # Enable Google reCAPTCHA recaptcha.enabled={{recaptcha.enabled}} -# Enable Google reCAPTCHA Enterprise -recaptcha.enterprise.enabled={{recaptcha.enterprise_enabled}} +# captcha type +recaptcha.type={{recaptcha.type}} # Forcefully enable Google reCAPTCHA for all tenants recaptcha.forcefullyEnabledForAllTenants={{recaptcha.forcefully_enabled_for_all_tenants}}