From 6d7f79b0b944151b0aea18294d396012ad578c0a Mon Sep 17 00:00:00 2001 From: Timo Lemke Date: Fri, 21 Apr 2023 12:29:42 +0200 Subject: [PATCH 1/2] refactoring the CaptchaService usage to allow different implementations than ReCaptcha without changing the code --- form-editor-cae/pom.xml | 1 - .../formeditor/cae/FormFreemarkerFacade.java | 10 ++--- .../cae/config/FormCaeConfiguration.java | 37 +++++++++++++++++++ ...aptchaService.java => CaptchaService.java} | 16 ++++---- .../cae/handler/FormController.java | 21 +++++------ .../cae/handler/ReCaptchaServiceImpl.java | 9 +++-- .../META-INF/coremedia/component-forms.xml | 9 ----- .../coremedia/form-freemarker-views.xml | 2 +- .../formeditor/cae/FormTestConfiguration.java | 14 +++---- 9 files changed, 74 insertions(+), 45 deletions(-) create mode 100644 form-editor-cae/src/main/java/com/tallence/formeditor/cae/config/FormCaeConfiguration.java rename form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/{ReCaptchaService.java => CaptchaService.java} (60%) diff --git a/form-editor-cae/pom.xml b/form-editor-cae/pom.xml index 46c5182e..01a34252 100644 --- a/form-editor-cae/pom.xml +++ b/form-editor-cae/pom.xml @@ -159,7 +159,6 @@ org.springframework.boot spring-boot-autoconfigure - test org.springframework.boot diff --git a/form-editor-cae/src/main/java/com/tallence/formeditor/cae/FormFreemarkerFacade.java b/form-editor-cae/src/main/java/com/tallence/formeditor/cae/FormFreemarkerFacade.java index e9c5a6ec..ba18c5d2 100644 --- a/form-editor-cae/src/main/java/com/tallence/formeditor/cae/FormFreemarkerFacade.java +++ b/form-editor-cae/src/main/java/com/tallence/formeditor/cae/FormFreemarkerFacade.java @@ -19,7 +19,7 @@ import com.coremedia.blueprint.common.services.context.CurrentContextService; import com.tallence.formeditor.FormEditorHelper; import com.tallence.formeditor.FormElementFactory; -import com.tallence.formeditor.cae.handler.ReCaptchaService; +import com.tallence.formeditor.cae.handler.CaptchaService; import com.tallence.formeditor.contentbeans.FormEditor; import com.tallence.formeditor.elements.FormElement; @@ -33,12 +33,12 @@ public class FormFreemarkerFacade { private final FormElementFactory formElementFactory; - private final ReCaptchaService reCaptchaService; + private final CaptchaService captchaService; private final CurrentContextService currentContextService; - public FormFreemarkerFacade(FormElementFactory formElementFactory, ReCaptchaService pReCaptchaService, CurrentContextService currentContextService) { - this.reCaptchaService = pReCaptchaService; + public FormFreemarkerFacade(FormElementFactory formElementFactory, CaptchaService pCaptchaService, CurrentContextService currentContextService) { + this.captchaService = pCaptchaService; this.formElementFactory = formElementFactory; this.currentContextService = currentContextService; } @@ -48,6 +48,6 @@ public List> parseFormElements(FormEditor formEditor) { } public String getReCaptchaWebsiteSecretForSite() { - return reCaptchaService.getWebsiteSecretForSite(currentContextService.getContext()); + return captchaService.getWebsiteSecretForSite(currentContextService.getContext()); } } diff --git a/form-editor-cae/src/main/java/com/tallence/formeditor/cae/config/FormCaeConfiguration.java b/form-editor-cae/src/main/java/com/tallence/formeditor/cae/config/FormCaeConfiguration.java new file mode 100644 index 00000000..05c68cf1 --- /dev/null +++ b/form-editor-cae/src/main/java/com/tallence/formeditor/cae/config/FormCaeConfiguration.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Tallence AG + * + * 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.tallence.formeditor.cae.config; + +import com.tallence.formeditor.cae.handler.CaptchaService; +import com.tallence.formeditor.cae.handler.ReCaptchaServiceImpl; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FormCaeConfiguration { + + @Bean + @ConditionalOnMissingBean(CaptchaService.class) + public ReCaptchaServiceImpl captchaService(@Value("${google.reCaptcha.website-secret}") String websiteSecret, + @Value("${google.reCaptcha.server-secret}") String serverSecret) { + + var auth = new ReCaptchaServiceImpl.ReCaptchaAuthentication(websiteSecret, serverSecret); + + return new ReCaptchaServiceImpl(auth); + } +} diff --git a/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/ReCaptchaService.java b/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/CaptchaService.java similarity index 60% rename from form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/ReCaptchaService.java rename to form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/CaptchaService.java index 2548baf4..ba487d6e 100644 --- a/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/ReCaptchaService.java +++ b/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/CaptchaService.java @@ -17,15 +17,15 @@ package com.tallence.formeditor.cae.handler; import com.coremedia.blueprint.common.contentbeans.CMNavigation; +import org.springframework.util.MultiValueMap; /** - * Encapsulates requests to google reCaptcha. - * The impl from CoreMedia elastic social Extension might be used. + * Encapsulates requests to a captcha service. */ -public interface ReCaptchaService { +public interface CaptchaService { /** - * Resolves the website-secret for google reCaptcha for the given Context. + * Resolves the website-secret for the captcha for the given Context. * * The context is currently ignored. Use it if you need multiple secrets for your sites. */ @@ -33,10 +33,10 @@ public interface ReCaptchaService { /** - * Asks the Google Recaptcha Service, if the given googleReCaptchaResponse is valid. - * @param googleReCaptchaResponse token, generated by the reCaptcha widget, by using the public key - * @return true, if the google reCaptcha service validates the request, false otherwise. + * Asks the Captcha Service, if the given response is valid. + * @param postData contains the captcha token, generated by the frontend widget + * @return true, if the captcha service validates the request, false otherwise. */ - boolean isHuman(String googleReCaptchaResponse, CMNavigation currentContext); + boolean isHuman(MultiValueMap postData, CMNavigation currentContext); } diff --git a/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/FormController.java b/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/FormController.java index 87bb606f..a6897f12 100644 --- a/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/FormController.java +++ b/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/FormController.java @@ -78,7 +78,7 @@ public class FormController { private final List formActions; private final DefaultFormAction defaultFormAction; - private final ReCaptchaService recaptchaService; + private final CaptchaService captchaService; private final FormFreemarkerFacade formFreemarkerFacade; private final CurrentContextService currentContextService; private final RequestMessageSource messageSource; @@ -87,7 +87,7 @@ public class FormController { public FormController(List formActions, DefaultFormAction defaultFormAction, - ReCaptchaService recaptchaService, + CaptchaService captchaService, FormFreemarkerFacade formFreemarkerFacade, CurrentContextService currentContextService, RequestMessageSource messageSource, @@ -95,7 +95,7 @@ public FormController(List formActions, @Value("${formEditor.cae.encodeData:true}") boolean encodeFormData) { this.formActions = formActions; this.defaultFormAction = defaultFormAction; - this.recaptchaService = recaptchaService; + this.captchaService = captchaService; this.formFreemarkerFacade = formFreemarkerFacade; this.currentContextService = currentContextService; this.messageSource = messageSource; @@ -165,8 +165,8 @@ public FormProcessingResult socialFormAction(@PathVariable(name = "currentContex } if (target.isSpamProtectionEnabled()) { - if (!isHumanByReCaptcha(target, navigation, postData)) { - LOG.warn("Google reCaptcha detected a bot for Form " + target.getContentId()); + if (!isHumanByCaptcha(target, navigation, postData)) { + LOG.warn("CaptchaService {} detected a bot for Form {}", captchaService.getClass().getName(), target.getContentId()); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return new FormProcessingResult( false, @@ -249,16 +249,15 @@ private MultipartFile processFileInput(MultipartHttpServletRequest multipartRequ /** * Validates the generated response-token in postData - * @param target form with reCaptcha - * @param postData includes the response-parameter, generated by the reCaptcha widget + * @param target form with captcha + * @param postData includes the response-parameter, generated by the captcha widget * @return true, if google says, the token is valid */ - private boolean isHumanByReCaptcha(FormEditor target, CMChannel currentContext, MultiValueMap postData) { + private boolean isHumanByCaptcha(FormEditor target, CMChannel currentContext, MultiValueMap postData) { try { - String googleReCaptchaResponse = postData.get("g-recaptcha-response").get(0); - return recaptchaService.isHuman(googleReCaptchaResponse, currentContext); + return captchaService.isHuman(postData, currentContext); } catch (Exception ex) { - LOG.error("Failed to verify reCapture via google for form " + target.getContentId(), ex); + LOG.error("Failed to verify captcha via {} for form {}", captchaService.getClass().getName(), target.getContentId(), ex); return false; } } diff --git a/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/ReCaptchaServiceImpl.java b/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/ReCaptchaServiceImpl.java index 135bd27e..0c9b62b9 100644 --- a/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/ReCaptchaServiceImpl.java +++ b/form-editor-cae/src/main/java/com/tallence/formeditor/cae/handler/ReCaptchaServiceImpl.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.MultiValueMap; import java.io.BufferedReader; import java.io.InputStream; @@ -19,7 +20,7 @@ /** * Encapsulates requests to google reCaptcha. */ -public class ReCaptchaServiceImpl implements ReCaptchaService { +public class ReCaptchaServiceImpl implements CaptchaService { private static final Logger LOG = LoggerFactory.getLogger(ReCaptchaServiceImpl.class); @@ -53,13 +54,15 @@ private String getServerSecretForSite(CMNavigation currentContext) { /** * Asks the Google Recaptcha Service, if the given googleReCaptchaResponse is valid. * - * @param googleReCaptchaResponse token, generated by the reCaptcha widget, by using the public key + * @param postData contains the token, generated by the reCaptcha widget, by using the public key * @return true, if the google reCaptcha service validates the request, false otherwise. */ - public boolean isHuman(String googleReCaptchaResponse, CMNavigation currentContext) { + @Override + public boolean isHuman(MultiValueMap postData, CMNavigation currentContext) { URLConnection connection; String query; + String googleReCaptchaResponse = postData.get("g-recaptcha-response").get(0); try { query = String.format("secret=%s&response=%s", URLEncoder.encode(getServerSecretForSite(currentContext), CHARSET_UTF_8), diff --git a/form-editor-cae/src/main/resources/META-INF/coremedia/component-forms.xml b/form-editor-cae/src/main/resources/META-INF/coremedia/component-forms.xml index a9e2b224..89f0f3e7 100644 --- a/form-editor-cae/src/main/resources/META-INF/coremedia/component-forms.xml +++ b/form-editor-cae/src/main/resources/META-INF/coremedia/component-forms.xml @@ -21,15 +21,6 @@ - - - - - - - - - diff --git a/form-editor-cae/src/main/resources/META-INF/coremedia/form-freemarker-views.xml b/form-editor-cae/src/main/resources/META-INF/coremedia/form-freemarker-views.xml index 0f973491..88a15ec1 100644 --- a/form-editor-cae/src/main/resources/META-INF/coremedia/form-freemarker-views.xml +++ b/form-editor-cae/src/main/resources/META-INF/coremedia/form-freemarker-views.xml @@ -18,7 +18,7 @@ - + formActions, DefaultFormAction defaultFormAction, - ReCaptchaService recaptchaService, + CaptchaService captchaService, FormFreemarkerFacade formFreemarkerFacade, CurrentContextService currentContextService, RequestMessageSource messageSource, ResourceBundleInterceptor pageResourceBundlesInterceptor, @Value("${formEditor.cae.encodeData:true}") boolean encodeFormData) { - return new FormController(formActions, defaultFormAction, recaptchaService, formFreemarkerFacade, currentContextService, messageSource, pageResourceBundlesInterceptor, encodeFormData); + return new FormController(formActions, defaultFormAction, captchaService, formFreemarkerFacade, currentContextService, messageSource, pageResourceBundlesInterceptor, encodeFormData); } @Bean From f0e451d8f8dd2baea4cee387055175239691cac8 Mon Sep 17 00:00:00 2001 From: Timo Lemke Date: Fri, 21 Apr 2023 12:52:50 +0200 Subject: [PATCH 2/2] update the readme --- README.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 751eadf6..55e15fa5 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,13 @@ The studio-frontend has been migrated into the new studio-pnpm-workspace structu The form-editor-studio-plugin can still be part of this external extensions repo and does not need to be moved into `apps/studio-client/apps/main/extensions/`. But it needs to be registered as an extension with the extensions-tool: Call `mvn extensions:sync -Denable=core-forms` in the folder `workspace-configuration/extensions` ## Integrate the Code in your CoreMedia Blueprint Workspace -You can integrate the extension in three ways: +You can integrate the extension in three ways. For all of them, you have to keep in mind: +* The formEditor uses maven-dependencies of the blueprint-workspace, keep the versions and groupIds in sync, e.g. "1-SNAPSHOT" and "com.coremedia.blueprint" +* After integrating the code, use the extension tool in the root folder of the project to link the modules into your workspace: + ``` +mvn -f workspace-configuration/extensions com.coremedia.maven:extensions-maven-plugin:LATEST:sync -Denable=core-forms +``` + **1. Git SubModule** @@ -44,26 +50,32 @@ Add this repo or your fork as a Git Submodule to your existing CoreMedia Bluepri This way, you will be able to merge new commits made in this repo back to your fork. -This is the recommended approach because you will also be able to develop quickly, performing a make on the sources with a running studio- or cae-webapp. +Use this approach if you are planing to contribute changes to the GitHub-Repo. From the project's root folder, clone this repository as submodule into the extensions folder. Make sure to use the branch name that matches your workspace version. ``` git submodule add https://github.com/tallence/core-forms.git modules/extensions/core-forms ``` -- Use the extension tool in the root folder of the project to link the modules into your workspace. - ``` -mvn -f workspace-configuration/extensions com.coremedia.maven:extensions-maven-plugin:LATEST:sync -Denable=core-forms -``` - -**2. Copy files** +**2. Git Subtree** -Download the repo and copy the files into your Blueprint-Workspace Extension-Folder. +Add this repo or your fork as a Git Subtree to your existing CoreMedia Blueprint-Workspace in the extensions-folder. +Use this approach if you are using the GitHub-Repo in a readOnly mode. + +To include the subtree into your project the following command line should be executed in the project root directory. + +```git subtree add --prefix modules/extensions/core-forms https://github.com/tallence/core-forms.git master --squash``` + +Further information about git subtree in relation to the github repository can be found here: -This way you won't be able to merge new commits made in this repo back to yours. But if you do not like Git Submodules, you don't have to deal with them. +[Github Subtree-Documentation](https://gist.github.com/SKempin/b7857a6ff6bddb05717cc17a44091202) -However the formeditor uses maven-dependencies of the blueprint-workspace, so you have to keep the versions in sync. - +**3. Copy files** + +Download the repo and copy the files into your Blueprint-Workspace Extension-Folder. + +This way you won't be able to merge new commits made in this repo back to yours. So this way is not recommended. + ## Required modifications To getting started you need to make some modifications and write some code: