Skip to content

Commit

Permalink
FORMS-17362 | Add Cloudflare Turnstile Support (#1487)
Browse files Browse the repository at this point in the history
* FORMS-17362 | Add Cloudflare Turnstile Support

* FORMS-17362 | Add FT in test cases

* FORMS-17362 | Addressing review comments

* FORMS-17362 | Bump up af-core & af-custom-functions

* FORMS-17362 | Add test for inline form in sites

* FORMS-17362 | Fix test for turnstile authoring

* FORMS-17362 | Fix test for recaptcha authoring

* FORMS-17362 | Addressing review comments

* FORMS-17362 | Addressing review comments

* FORMS-17362 | Addressing review comments

* FORMS-17362 | Fixing doc string

* FORMS-17362 | Fix gh exporter-validate

* FORMS-17362 | Move @JsonIgnore to interface and abstract class

* Improving coverage

---------

Co-authored-by: Navneet Agarwal <[email protected]>
Co-authored-by: Rishi Mehta <[email protected]>
  • Loading branch information
3 people authored Dec 5, 2024
1 parent 2207d43 commit 07639b3
Show file tree
Hide file tree
Showing 57 changed files with 2,374 additions and 84 deletions.
43 changes: 24 additions & 19 deletions .github/workflows/exporter-validate-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,39 @@ jobs:
# Loop through each changed file
for file in $changed_files; do
# Fetch the base and head versions of the file
base_file=$(git show ${{ github.base_ref }}:$file)
head_file=$(git show ${{ github.head_ref }}:$file)
# Check if the file exists in both branches
if git cat-file -e origin/${{ github.base_ref }}:$file 2>/dev/null && git cat-file -e origin/${{ github.head_ref }}:$file 2>/dev/null; then
# Fetch the base and head versions of the file
base_file=$(git show origin/${{ github.base_ref }}:$file)
head_file=$(git show origin/${{ github.head_ref }}:$file)
# Compare the JSON keys
base_keys=$(echo "$base_file" | jq -r 'paths | map(tostring) | join(".")' | sed 's/\./\\./g')
head_keys=$(echo "$head_file" | jq -r 'paths | map(tostring) | join(".")' | sed 's/\./\\./g')
# Compare the JSON keys
base_keys=$(echo "$base_file" | jq -r 'paths | map(tostring) | join(".")' | sed 's/\./\\./g')
head_keys=$(echo "$head_file" | jq -r 'paths | map(tostring) | join(".")' | sed 's/\./\\./g')
# Check for removed keys
removed_keys=$(comm -23 <(echo "$base_keys" | sort) <(echo "$head_keys" | sort))
# Check for removed keys
removed_keys=$(comm -23 <(echo "$base_keys" | sort) <(echo "$head_keys" | sort))
if [ -n "$removed_keys" ]; then
echo "Backward incompatibility change detected in $file. The following keys were removed:"
echo "$removed_keys"
exit 1
fi
if [ -n "$removed_keys" ]; then
echo "Backward incompatibility change detected in $file. The following keys were removed:"
echo "$removed_keys"
exit 1
fi
# Check for changed values
for key in $base_keys; do
base_value=$(echo "$base_file" | jq -r --arg key "$key" '.[$key]')
head_value=$(echo "$head_file" | jq -r --arg key "$key" '.[$key]')
if [ "$base_value" != "$head_value" ]; then
echo "Backward incompatibility change detected in $file. The value of key '$key' was changed from '$base_value' to '$head_value'."
exit 1
fi
done
if [ "$base_value" != "$head_value" ]; then
echo "Backward incompatibility change detected in $file. The value of key '$key' was changed from '$base_value' to '$head_value'."
exit 1
fi
done
else
echo "Skipping file $file as it exists in one branch but not the other."
fi
done
echo "All exporter JSON files have only additions. No backward incompatibility changes detected."
shell: bash
shell: bash
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ private FormConstants() {
/** The resource type for check box group v1 */
public static final String RT_FD_FORM_CHECKBOX_GROUP_V1 = RT_FD_FORM_PREFIX + "checkboxgroup/v1/checkboxgroup";

/** The resource type for reCaptcha v1 */
/** The resource type for turnstile v1 */
public static final String RT_FD_FORM_TURNSTILE_V1 = RT_FD_FORM_PREFIX + "turnstile/v1/turnstile";

/** The resource type for hCaptcha v1 */
public static final String RT_FD_FORM_HCAPTCHA_V1 = RT_FD_FORM_PREFIX + "hcaptcha/v1/hcaptcha";

/** The resource type for reCaptcha v1 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import com.adobe.cq.forms.core.components.internal.form.ReservedProperties;
import com.adobe.cq.forms.core.components.models.form.HCaptcha;
import com.adobe.cq.forms.core.components.util.AbstractCaptchaImpl;
import com.fasterxml.jackson.annotation.JsonIgnore;

@Model(
adaptables = { SlingHttpServletRequest.class, Resource.class },
Expand All @@ -68,26 +67,23 @@ public class HCaptchaImpl extends AbstractCaptchaImpl implements HCaptcha {
private CloudConfigurationProvider cloudConfigurationProvider;

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
@JsonIgnore
@Named(ReservedProperties.PN_CLOUD_SERVICE_PATH)
protected String cloudServicePath;

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
@JsonIgnore
@Named(ReservedProperties.PN_SIZE)
protected String size;

private static final String SITE_KEY = "siteKey";
private static final String URI = "uri";
private static final String SIZE = "size";
private static final String THEME = "theme";
private static final String TYPE = "type";

@Override
public String getCloudServicePath() {
return cloudServicePath;
}

@Override
public String getSize() {
return size;
}

@Override
public String getProvider() {
return "hcaptcha";
Expand All @@ -113,11 +109,11 @@ public Map<String, Object> getCaptchaProperties() throws GuideException {
} catch (GuideException e) {
LOGGER.error("[AF] [Captcha] [HCAPTCHA] Error while fetching cloud configuration, upgrade to latest release to use hCaptcha.");
}
customCaptchaProperties.put(SITE_KEY, siteKey);
customCaptchaProperties.put(URI, uri);
customCaptchaProperties.put(SIZE, this.size);
customCaptchaProperties.put(THEME, "light");
customCaptchaProperties.put(TYPE, "image");
customCaptchaProperties.put(CAPTCHA_SITE_KEY, siteKey);
customCaptchaProperties.put(CAPTCHA_URI, uri);
customCaptchaProperties.put(CAPTCHA_SIZE, getSize());
customCaptchaProperties.put(CAPTCHA_THEME, "light");
customCaptchaProperties.put(CAPTCHA_TYPE, "image");

return customCaptchaProperties;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import com.adobe.cq.forms.core.components.internal.form.ReservedProperties;
import com.adobe.cq.forms.core.components.models.form.Captcha;
import com.adobe.cq.forms.core.components.util.AbstractCaptchaImpl;
import com.fasterxml.jackson.annotation.JsonIgnore;

@Model(
adaptables = { SlingHttpServletRequest.class, Resource.class },
Expand All @@ -65,33 +64,25 @@ public class RecaptchaImpl extends AbstractCaptchaImpl implements Captcha {
private CloudConfigurationProvider cloudConfigurationProvider;

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
@JsonIgnore
@Named(ReservedProperties.PN_RECAPTCHA_CLOUD_SERVICE_PATH)
protected String cloudServicePath;

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
@JsonIgnore
@Named(ReservedProperties.PN_RECAPTCHA_SIZE)
protected String size;

public static final String RECAPTCHA_DEFAULT_DOMAIN = "https://www.recaptcha.net/";
public static final String RECAPTCHA_DEFAULT_URL = RECAPTCHA_DEFAULT_DOMAIN + "recaptcha/api.js";
public static final String RECAPTCHA_ENTERPRISE_DEFAULT_URL = RECAPTCHA_DEFAULT_DOMAIN + "recaptcha/enterprise.js";
private static final String RECAPTCHA_SITE_KEY = "siteKey";
private static final String RECAPTCHA_URI = "uri";
private static final String RECAPTCHA_SIZE = "size";
private static final String RECAPTCHA_THEME = "theme";
private static final String RECAPTCHA_TYPE = "type";
private static final String RECAPTCHA_VERSION = "version";
private static final String RECAPTCHA_KEYTYPE = "keyType";
public static final String RECAPTCHA_VERSION = "version";
public static final String RECAPTCHA_KEYTYPE = "keyType";

@Override
@JsonIgnore
public String getCloudServicePath() {
return cloudServicePath;
}

@JsonIgnore
@Override
public String getSize() {
return size;
}
Expand All @@ -101,7 +92,6 @@ public String getProvider() {
return "recaptcha";
}

@JsonIgnore
@Override
public Map<String, Object> getCaptchaProperties() throws GuideException {

Expand All @@ -118,20 +108,19 @@ public Map<String, Object> getCaptchaProperties() throws GuideException {
keyType = reCaptchaConfiguration.keyType();
}
}
customCaptchaProperties.put(RECAPTCHA_SITE_KEY, siteKey);
customCaptchaProperties.put(CAPTCHA_SITE_KEY, siteKey);
if (StringUtils.isNotEmpty(version) && version.equals("enterprise")) {
customCaptchaProperties.put(RECAPTCHA_URI, RECAPTCHA_ENTERPRISE_DEFAULT_URL);
customCaptchaProperties.put(CAPTCHA_URI, RECAPTCHA_ENTERPRISE_DEFAULT_URL);
} else {
customCaptchaProperties.put(RECAPTCHA_URI, RECAPTCHA_DEFAULT_URL);
customCaptchaProperties.put(CAPTCHA_URI, RECAPTCHA_DEFAULT_URL);
}
customCaptchaProperties.put(RECAPTCHA_SIZE, getSize());
customCaptchaProperties.put(RECAPTCHA_THEME, "light");
customCaptchaProperties.put(RECAPTCHA_TYPE, "image");
customCaptchaProperties.put(CAPTCHA_SIZE, getSize());
customCaptchaProperties.put(CAPTCHA_THEME, "light");
customCaptchaProperties.put(CAPTCHA_TYPE, "image");
customCaptchaProperties.put(RECAPTCHA_VERSION, version);
customCaptchaProperties.put(RECAPTCHA_KEYTYPE, keyType);

return customCaptchaProperties;

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Copyright 2024 Adobe
~
~ Licensed 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 com.adobe.cq.forms.core.components.internal.models.v1.form;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.aemds.guide.model.TurnstileConfiguration;
import com.adobe.aemds.guide.service.CloudConfigurationProvider;
import com.adobe.aemds.guide.service.GuideException;
import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.forms.core.components.internal.form.FormConstants;
import com.adobe.cq.forms.core.components.models.form.Turnstile;
import com.adobe.cq.forms.core.components.util.AbstractCaptchaImplV2;

@Model(
adaptables = { SlingHttpServletRequest.class, Resource.class },
adapters = { Turnstile.class,
ComponentExporter.class },
resourceType = { FormConstants.RT_FD_FORM_TURNSTILE_V1 })
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class TurnstileImpl extends AbstractCaptchaImplV2 implements Turnstile {
private static final Logger LOGGER = LoggerFactory.getLogger(TurnstileImpl.class);

@Inject
private ResourceResolver resourceResolver;

private Resource resource;
private String captchaSiteKey;

@Reference
@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
private TurnstileConfiguration turnstileConfiguration;

@OSGiService
@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
private CloudConfigurationProvider cloudConfigurationProvider;

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
@Named("cloudServicePath")
protected String cloudServicePath;

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
@Named("size")
protected String size;

@Override
public String getCloudServicePath() {
return cloudServicePath;
}

@Override
public String getProvider() {
return "turnstile";
}

@Override
public String getSize() {
return size;
}

/**
* Set the turnstileConfiguration, by fetching it from the cloud configurations.
* Also sets the captchaSiteKey.
*/
private void setTurnstileConfiguration() {
LOGGER.debug("[AF] [Captcha] [TURNSTILE] Fetching cloud configuration for turnstile.");
if (cloudConfigurationProvider != null) {
try {
resource = resourceResolver.getResource(this.getPath());
turnstileConfiguration = cloudConfigurationProvider.getTurnstileCloudConfiguration(resource);
if (turnstileConfiguration != null) {
captchaSiteKey = turnstileConfiguration.getSiteKey();
} else {
LOGGER.debug("[AF] [Captcha] [TURNSTILE] Cloud configuration for turnstile is not available for " + this.getPath());

Check warning on line 106 in bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TurnstileImpl.java

View check run for this annotation

Codecov / codecov/patch

bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TurnstileImpl.java#L106

Added line #L106 was not covered by tests
}
} catch (GuideException e) {
LOGGER.error(
"[AF] [Captcha] [TURNSTILE] Error while fetching cloud configuration, upgrade to latest release to use turnstile.", e);
}
} else {
LOGGER.error(

Check warning on line 113 in bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TurnstileImpl.java

View check run for this annotation

Codecov / codecov/patch

bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TurnstileImpl.java#L113

Added line #L113 was not covered by tests
"[AF] [Captcha] [TURNSTILE] Error while fetching cloud configuration, upgrade to latest release to use turnstile.");
}
}

@PostConstruct
@Override
public Map<String, Object> getCaptchaProperties() {
Map<String, Object> customCaptchaProperties = new LinkedHashMap<>();
String siteKey = null, uri = null, widgetType = null;
if (turnstileConfiguration == null) {
setTurnstileConfiguration();
}
if (turnstileConfiguration != null) {
customCaptchaProperties.put(CAPTCHA_URI, turnstileConfiguration.getClientSideJsUrl());
customCaptchaProperties.put(CAPTCHA_WIDGET_TYPE, turnstileConfiguration.getWidgetType());
}
customCaptchaProperties.put(CAPTCHA_SIZE, getSize());
customCaptchaProperties.put(CAPTCHA_THEME, CAPTCHA_THEME_LIGHT);
return customCaptchaProperties;
}

@PostConstruct
@Override
public String getCaptchaDisplayMode() {
CaptchaDisplayMode captchaDisplayMode = CaptchaDisplayMode.VISIBLE;
if (turnstileConfiguration == null) {
setTurnstileConfiguration();

Check warning on line 140 in bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TurnstileImpl.java

View check run for this annotation

Codecov / codecov/patch

bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TurnstileImpl.java#L140

Added line #L140 was not covered by tests
}
if (turnstileConfiguration != null && CaptchaDisplayMode.INVISIBLE.getValue().equals(turnstileConfiguration.getWidgetType())) {
captchaDisplayMode = CaptchaDisplayMode.INVISIBLE;
}
return captchaDisplayMode.getValue();
}

@PostConstruct
@Override
public String getCaptchaSiteKey() {
if (turnstileConfiguration == null) {
setTurnstileConfiguration();

Check warning on line 152 in bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TurnstileImpl.java

View check run for this annotation

Codecov / codecov/patch

bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/TurnstileImpl.java#L152

Added line #L152 was not covered by tests
}
return this.captchaSiteKey;
}
}
Loading

0 comments on commit 07639b3

Please sign in to comment.