From 3a2fab1b5c5c88df34318d64b94fbeafcf0050a1 Mon Sep 17 00:00:00 2001 From: Chirag Chhatrala Date: Mon, 16 Dec 2024 11:21:22 +0530 Subject: [PATCH 1/8] Add reCAPTCHA support and update captcha provider handling - Introduced reCAPTCHA as an additional captcha provider alongside hCaptcha. - Updated form request validation to handle different captcha providers based on user selection. - Added a new validation rule for reCAPTCHA. - Modified the forms model to include a 'captcha_provider' field. - Created a migration to add the 'captcha_provider' column to the forms table. - Updated frontend components to support dynamic rendering of captcha based on the selected provider. - Enhanced tests to cover scenarios for both hCaptcha and reCAPTCHA. These changes improve the flexibility of captcha options available to users, enhancing form security and user experience. --- api/.env.example | 3 + api/app/Http/Requests/AnswerFormRequest.php | 7 +- api/app/Http/Requests/UserFormRequest.php | 1 + api/app/Models/Forms/Form.php | 1 + api/app/Rules/ValidReCaptcha.php | 51 +++++++++++++ api/config/services.php | 5 ++ ...00_add_captcha_provider_to_forms_table.php | 22 ++++++ api/tests/Feature/Forms/FormCaptchaTest.php | 23 +++++- client/.env.example | 1 + client/components/forms/RecaptchaV2.vue | 76 +++++++++++++++++++ client/components/open/forms/OpenForm.vue | 54 +++++++++---- .../form-components/FormSecurityAccess.vue | 29 +++---- client/runtimeConfig.js | 1 + 13 files changed, 245 insertions(+), 29 deletions(-) create mode 100644 api/app/Rules/ValidReCaptcha.php create mode 100644 api/database/migrations/2024_03_21_000000_add_captcha_provider_to_forms_table.php create mode 100644 client/components/forms/RecaptchaV2.vue diff --git a/api/.env.example b/api/.env.example index 31762cf80..445431f0c 100644 --- a/api/.env.example +++ b/api/.env.example @@ -72,6 +72,9 @@ STRIPE_TEST_DEFAULT_PRICING_YEARLY= H_CAPTCHA_SITE_KEY= H_CAPTCHA_SECRET_KEY= +RE_CAPTCHA_SITE_KEY= +RE_CAPTCHA_SECRET_KEY= + MUX_WORKSPACE_ID= MUX_API_TOKEN= diff --git a/api/app/Http/Requests/AnswerFormRequest.php b/api/app/Http/Requests/AnswerFormRequest.php index 0edc1989d..89aedfbbf 100644 --- a/api/app/Http/Requests/AnswerFormRequest.php +++ b/api/app/Http/Requests/AnswerFormRequest.php @@ -8,6 +8,7 @@ use App\Rules\StorageFile; use App\Rules\ValidHCaptcha; use App\Rules\ValidPhoneInputRule; +use App\Rules\ValidReCaptcha; use App\Rules\ValidUrl; use App\Service\Forms\FormLogicPropertyResolver; use Illuminate\Foundation\Http\FormRequest; @@ -118,7 +119,11 @@ public function rules() // Validate hCaptcha if ($this->form->use_captcha) { - $this->requestRules['h-captcha-response'] = [new ValidHCaptcha()]; + if ($this->form->captcha_provider === 'recaptcha') { + $this->requestRules['g-recaptcha-response'] = [new ValidReCaptcha()]; + } elseif ($this->form->captcha_provider === 'hcaptcha') { + $this->requestRules['h-captcha-response'] = [new ValidHCaptcha()]; + } } // Validate submission_id for edit mode diff --git a/api/app/Http/Requests/UserFormRequest.php b/api/app/Http/Requests/UserFormRequest.php index cfaa84162..f8b8da295 100644 --- a/api/app/Http/Requests/UserFormRequest.php +++ b/api/app/Http/Requests/UserFormRequest.php @@ -118,6 +118,7 @@ public function rules() 'can_be_indexed' => 'boolean', 'password' => 'sometimes|nullable', 'use_captcha' => 'boolean', + 'captcha_provider' => ['sometimes', Rule::in(['recaptcha', 'hcaptcha'])], // Custom SEO 'seo_meta' => 'nullable|array', diff --git a/api/app/Models/Forms/Form.php b/api/app/Models/Forms/Form.php index f8769a2ee..541723710 100644 --- a/api/app/Models/Forms/Form.php +++ b/api/app/Models/Forms/Form.php @@ -82,6 +82,7 @@ class Form extends Model implements CachableAttributes 'submitted_text', 'redirect_url', 'use_captcha', + 'captcha_provider', 'closes_at', 'closed_text', 'max_submissions_count', diff --git a/api/app/Rules/ValidReCaptcha.php b/api/app/Rules/ValidReCaptcha.php new file mode 100644 index 000000000..2bd86732a --- /dev/null +++ b/api/app/Rules/ValidReCaptcha.php @@ -0,0 +1,51 @@ +error = 'Please complete the captcha.'; + + return false; + } + + return Http::asForm()->post(self::RECAPTCHA_VERIFY_URL, [ + 'secret' => config('services.re_captcha.secret_key'), + 'response' => $value, + ])->json('success'); + } + public function validate(string $attribute, mixed $value, Closure $fail): void + { + if (!$this->passes($attribute, $value)) { + $fail($this->message()); + } + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return $this->error; + } +} diff --git a/api/config/services.php b/api/config/services.php index 8076ef9a2..eda2e4708 100644 --- a/api/config/services.php +++ b/api/config/services.php @@ -40,6 +40,11 @@ 'secret_key' => env('H_CAPTCHA_SECRET_KEY'), ], + 're_captcha' => [ + 'site_key' => env('RE_CAPTCHA_SITE_KEY'), + 'secret_key' => env('RE_CAPTCHA_SECRET_KEY'), + ], + 'canny' => [ 'api_key' => env('CANNY_API_KEY'), ], diff --git a/api/database/migrations/2024_03_21_000000_add_captcha_provider_to_forms_table.php b/api/database/migrations/2024_03_21_000000_add_captcha_provider_to_forms_table.php new file mode 100644 index 000000000..53b51fda0 --- /dev/null +++ b/api/database/migrations/2024_03_21_000000_add_captcha_provider_to_forms_table.php @@ -0,0 +1,22 @@ +string('captcha_provider')->default('hcaptcha')->after('use_captcha'); + }); + } + + public function down() + { + Schema::table('forms', function (Blueprint $table) { + $table->dropColumn('captcha_provider'); + }); + } +}; diff --git a/api/tests/Feature/Forms/FormCaptchaTest.php b/api/tests/Feature/Forms/FormCaptchaTest.php index 7b3308259..84b74cbfc 100644 --- a/api/tests/Feature/Forms/FormCaptchaTest.php +++ b/api/tests/Feature/Forms/FormCaptchaTest.php @@ -1,10 +1,11 @@ actingAsUser(); $workspace = $this->createUserWorkspace($user); $form = $this->createForm($user, $workspace, [ 'use_captcha' => true, + 'captcha_provider' => 'hcaptcha', ]); $this->postJson(route('forms.answer', $form->slug), []) @@ -18,3 +19,23 @@ ], ]); }); + +it('create form with recaptcha and raise validation issue', function () { + $user = $this->actingAsUser(); + $workspace = $this->createUserWorkspace($user); + $form = $this->createForm($user, $workspace, [ + 'use_captcha' => true, + 'captcha_provider' => 'recaptcha', + ]); + + $this->postJson(route('forms.answer', $form->slug), []) + ->assertStatus(422) + ->assertJson([ + 'message' => 'Please complete the captcha.', + 'errors' => [ + 'g-recaptcha-response' => [ + 'Please complete the captcha.', + ], + ], + ]); +}); diff --git a/client/.env.example b/client/.env.example index 7fe5fcf22..261d3e267 100644 --- a/client/.env.example +++ b/client/.env.example @@ -6,4 +6,5 @@ NUXT_PUBLIC_CRISP_WEBSITE_ID= NUXT_PUBLIC_ENV= NUXT_PUBLIC_GOOGLE_ANALYTICS_CODE= NUXT_PUBLIC_H_CAPTCHA_SITE_KEY= +NUXT_PUBLIC_RE_CAPTCHA_SITE_KEY= NUXT_API_SECRET=secret diff --git a/client/components/forms/RecaptchaV2.vue b/client/components/forms/RecaptchaV2.vue new file mode 100644 index 000000000..af6e9485c --- /dev/null +++ b/client/components/forms/RecaptchaV2.vue @@ -0,0 +1,76 @@ + + + \ No newline at end of file diff --git a/client/components/open/forms/OpenForm.vue b/client/components/open/forms/OpenForm.vue index 4589bb71f..55953833f 100644 --- a/client/components/open/forms/OpenForm.vue +++ b/client/components/open/forms/OpenForm.vue @@ -78,17 +78,31 @@ @@ -214,6 +228,9 @@ export default { hCaptchaSiteKey() { return useRuntimeConfig().public.hCaptchaSiteKey }, + recaptchaSiteKey() { + return useRuntimeConfig().public.recaptchaSiteKey + }, /** * Create field groups (or Page) using page breaks if any */ @@ -369,8 +386,13 @@ export default { if (!this.isAutoSubmit && this.formPageIndex !== this.fieldGroups.length - 1) return if (this.form.use_captcha && import.meta.client) { - this.dataForm['h-captcha-response'] = document.getElementsByName('h-captcha-response')[0].value - this.$refs.hcaptcha.reset() + if (this.form.captcha_provider === 'recaptcha') { + this.dataForm['g-recaptcha-response'] = document.getElementsByName('g-recaptcha-response')[0]?.value + window.grecaptcha?.reset() + } else if (this.form.captcha_provider === 'hcaptcha') { + this.dataForm['h-captcha-response'] = document.getElementsByName('h-captcha-response')[0].value + this.$refs.hcaptcha.reset() + } } if (this.form.editable_submissions && this.form.submission_id) { @@ -593,6 +615,12 @@ export default { } catch (e) { console.error(e) } + }, + onRecaptchaVerify(token) { + this.dataForm['g-recaptcha-response'] = token + }, + onRecaptchaExpired() { + this.dataForm['g-recaptcha-response'] = null } } } diff --git a/client/components/open/forms/components/form-components/FormSecurityAccess.vue b/client/components/open/forms/components/form-components/FormSecurityAccess.vue index 8be4b9f77..f535807bd 100644 --- a/client/components/open/forms/components/form-components/FormSecurityAccess.vue +++ b/client/components/open/forms/components/form-components/FormSecurityAccess.vue @@ -79,22 +79,23 @@ label="Bot Protection" help="Protects your form from spam and abuse with a captcha" /> + - diff --git a/client/runtimeConfig.js b/client/runtimeConfig.js index 968a5f5f1..d094fb68e 100644 --- a/client/runtimeConfig.js +++ b/client/runtimeConfig.js @@ -19,6 +19,7 @@ export default { appUrl: process.env.NUXT_PUBLIC_APP_URL || '', env: process.env.NUXT_PUBLIC_ENV || 'local', hCaptchaSiteKey: process.env.NUXT_PUBLIC_H_CAPTCHA_SITE_KEY || null, + recaptchaSiteKey: process.env.NUXT_PUBLIC_RE_CAPTCHA_SITE_KEY || null, gtmCode: process.env.NUXT_PUBLIC_GTM_CODE || null, amplitudeCode: process.env.NUXT_PUBLIC_AMPLITUDE_CODE || null, crispWebsiteId: process.env.NUXT_PUBLIC_CRISP_WEBSITE_ID || null, From 79458731b4f59bdffba9ac4ae862f2512f74f989 Mon Sep 17 00:00:00 2001 From: Chirag Chhatrala Date: Mon, 16 Dec 2024 11:22:22 +0530 Subject: [PATCH 2/8] fix pint --- .../2024_03_21_000000_add_captcha_provider_to_forms_table.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/database/migrations/2024_03_21_000000_add_captcha_provider_to_forms_table.php b/api/database/migrations/2024_03_21_000000_add_captcha_provider_to_forms_table.php index 53b51fda0..1e535425e 100644 --- a/api/database/migrations/2024_03_21_000000_add_captcha_provider_to_forms_table.php +++ b/api/database/migrations/2024_03_21_000000_add_captcha_provider_to_forms_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class () extends Migration { public function up() { Schema::table('forms', function (Blueprint $table) { From f0e065d69299c75e6333cacc16a9d19cb3a3de8a Mon Sep 17 00:00:00 2001 From: Chirag Chhatrala Date: Mon, 16 Dec 2024 11:38:16 +0530 Subject: [PATCH 3/8] change comment text --- api/app/Http/Requests/AnswerFormRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/app/Http/Requests/AnswerFormRequest.php b/api/app/Http/Requests/AnswerFormRequest.php index 89aedfbbf..722f48fbd 100644 --- a/api/app/Http/Requests/AnswerFormRequest.php +++ b/api/app/Http/Requests/AnswerFormRequest.php @@ -117,7 +117,7 @@ public function rules() $this->requestRules[$propertyId] = $rules; } - // Validate hCaptcha + // Validate Captcha if ($this->form->use_captcha) { if ($this->form->captcha_provider === 'recaptcha') { $this->requestRules['g-recaptcha-response'] = [new ValidReCaptcha()]; From dfc48255a555b17594ac22eb8f1fe932b07a8fd7 Mon Sep 17 00:00:00 2001 From: Chirag Chhatrala Date: Wed, 18 Dec 2024 12:18:43 +0530 Subject: [PATCH 4/8] Refactor captcha implementation and integrate new captcha components - Removed the old RecaptchaV2 component and replaced it with a new implementation that supports both reCAPTCHA and hCaptcha through a unified CaptchaInput component. - Updated the OpenForm component to utilize the new CaptchaInput for dynamic captcha rendering based on user-selected provider. - Cleaned up the package.json by removing the deprecated @hcaptcha/vue3-hcaptcha dependency. - Enhanced form initialization to set a default captcha provider. - Improved error handling and cleanup for both reCAPTCHA and hCaptcha scripts. These changes streamline captcha integration, improve maintainability, and enhance user experience by providing a more flexible captcha solution. --- client/components/forms/RecaptchaV2.vue | 76 ------- .../forms/components/CaptchaInput.vue | 179 +++++++++++++++++ .../forms/components/HCaptchaV2.vue | 186 ++++++++++++++++++ .../forms/components/RecaptchaV2.vue | 179 +++++++++++++++++ client/components/open/forms/OpenForm.vue | 75 ++----- .../form-components/FormSecurityAccess.vue | 32 +-- client/composables/forms/initForm.js | 2 + client/package.json | 1 - 8 files changed, 576 insertions(+), 154 deletions(-) delete mode 100644 client/components/forms/RecaptchaV2.vue create mode 100644 client/components/forms/components/CaptchaInput.vue create mode 100644 client/components/forms/components/HCaptchaV2.vue create mode 100644 client/components/forms/components/RecaptchaV2.vue diff --git a/client/components/forms/RecaptchaV2.vue b/client/components/forms/RecaptchaV2.vue deleted file mode 100644 index af6e9485c..000000000 --- a/client/components/forms/RecaptchaV2.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - \ No newline at end of file diff --git a/client/components/forms/components/CaptchaInput.vue b/client/components/forms/components/CaptchaInput.vue new file mode 100644 index 000000000..d38647f3b --- /dev/null +++ b/client/components/forms/components/CaptchaInput.vue @@ -0,0 +1,179 @@ + + + + + \ No newline at end of file diff --git a/client/components/forms/components/HCaptchaV2.vue b/client/components/forms/components/HCaptchaV2.vue new file mode 100644 index 000000000..18d2d4bf9 --- /dev/null +++ b/client/components/forms/components/HCaptchaV2.vue @@ -0,0 +1,186 @@ + + + \ No newline at end of file diff --git a/client/components/forms/components/RecaptchaV2.vue b/client/components/forms/components/RecaptchaV2.vue new file mode 100644 index 000000000..885077876 --- /dev/null +++ b/client/components/forms/components/RecaptchaV2.vue @@ -0,0 +1,179 @@ + + + \ No newline at end of file diff --git a/client/components/open/forms/OpenForm.vue b/client/components/open/forms/OpenForm.vue index 55953833f..30caa001a 100644 --- a/client/components/open/forms/OpenForm.vue +++ b/client/components/open/forms/OpenForm.vue @@ -76,35 +76,16 @@ - +
+ +
@@ -146,7 +127,7 @@ import clonedeep from 'clone-deep' import draggable from 'vuedraggable' import OpenFormButton from './OpenFormButton.vue' -import VueHcaptcha from "@hcaptcha/vue3-hcaptcha" +import CaptchaInput from '~/components/forms/components/CaptchaInput.vue' import OpenFormField from './OpenFormField.vue' import {pendingSubmission} from "~/composables/forms/pendingSubmission.js" import FormLogicPropertyResolver from "~/lib/forms/FormLogicPropertyResolver.js" @@ -157,7 +138,7 @@ import { storeToRefs } from 'pinia' export default { name: 'OpenForm', - components: {draggable, OpenFormField, OpenFormButton, VueHcaptcha, FormTimer}, + components: {draggable, OpenFormField, OpenFormButton, CaptchaInput, FormTimer}, props: { form: { type: Object, @@ -220,17 +201,10 @@ export default { * Used to force refresh components by changing their keys */ isAutoSubmit: false, - minHeight: 0 } }, computed: { - hCaptchaSiteKey() { - return useRuntimeConfig().public.hCaptchaSiteKey - }, - recaptchaSiteKey() { - return useRuntimeConfig().public.recaptchaSiteKey - }, /** * Create field groups (or Page) using page breaks if any */ @@ -326,7 +300,6 @@ export default { }, computedStyle() { return { - ...this.minHeight ? {minHeight: this.minHeight + 'px'} : {}, '--form-color': this.form.color } } @@ -386,13 +359,7 @@ export default { if (!this.isAutoSubmit && this.formPageIndex !== this.fieldGroups.length - 1) return if (this.form.use_captcha && import.meta.client) { - if (this.form.captcha_provider === 'recaptcha') { - this.dataForm['g-recaptcha-response'] = document.getElementsByName('g-recaptcha-response')[0]?.value - window.grecaptcha?.reset() - } else if (this.form.captcha_provider === 'hcaptcha') { - this.dataForm['h-captcha-response'] = document.getElementsByName('h-captcha-response')[0].value - this.$refs.hcaptcha.reset() - } + this.$refs.captcha?.reset() } if (this.form.editable_submissions && this.form.submission_id) { @@ -606,22 +573,6 @@ export default { } this.formPageIndex = this.fieldGroups.length - 1 }, - setMinHeight(minHeight) { - if (!this.isIframe) return - - this.minHeight = minHeight - try { - window.parentIFrame.size() - } catch (e) { - console.error(e) - } - }, - onRecaptchaVerify(token) { - this.dataForm['g-recaptcha-response'] = token - }, - onRecaptchaExpired() { - this.dataForm['g-recaptcha-response'] = null - } } } diff --git a/client/components/open/forms/components/form-components/FormSecurityAccess.vue b/client/components/open/forms/components/form-components/FormSecurityAccess.vue index f535807bd..2a5edea3b 100644 --- a/client/components/open/forms/components/form-components/FormSecurityAccess.vue +++ b/client/components/open/forms/components/form-components/FormSecurityAccess.vue @@ -72,21 +72,23 @@

Protect your form, and your sensitive files.

- - +
+ + +
diff --git a/client/composables/forms/initForm.js b/client/composables/forms/initForm.js index 477268497..869c3a21d 100644 --- a/client/composables/forms/initForm.js +++ b/client/composables/forms/initForm.js @@ -36,6 +36,7 @@ export const initForm = (defaultValue = {}, withDefaultProperties = false) => { submitted_text: "Amazing, we saved your answers. Thank you for your time and have a great day!", use_captcha: false, + captcha_provider: 'recaptcha', max_submissions_count: null, max_submissions_reached_text: "This form has now reached the maximum number of allowed submissions and is now closed.", @@ -105,6 +106,7 @@ export function setFormDefaults(formData) { bypass_success_page: false, can_be_indexed: true, use_captcha: false, + captcha_provider: 'recaptcha', properties: [], } diff --git a/client/package.json b/client/package.json index 9f9d62433..d39bcef8b 100644 --- a/client/package.json +++ b/client/package.json @@ -36,7 +36,6 @@ }, "dependencies": { "@codemirror/lang-html": "^6.4.9", - "@hcaptcha/vue3-hcaptcha": "^1.3.0", "@iconify-json/material-symbols": "^1.2.4", "@nuxt/ui": "^2.19.2", "@pinia/nuxt": "^0.5.5", From 60c720d4d1bd344c662a0c90a4e02177335c2ba6 Mon Sep 17 00:00:00 2001 From: Chirag Chhatrala Date: Wed, 18 Dec 2024 15:33:40 +0530 Subject: [PATCH 5/8] Refactor captcha error messages and localization support --- api/app/Rules/ValidHCaptcha.php | 6 +++--- api/app/Rules/ValidReCaptcha.php | 6 +++--- api/resources/lang/ar/validation.php | 2 ++ api/resources/lang/bn/validation.php | 2 ++ api/resources/lang/de/validation.php | 2 ++ api/resources/lang/en/validation.php | 2 ++ api/resources/lang/es/validation.php | 2 ++ api/resources/lang/fr/validation.php | 2 ++ api/resources/lang/hi/validation.php | 2 ++ api/resources/lang/ja/validation.php | 2 ++ api/resources/lang/jv/validation.php | 2 ++ api/resources/lang/ko/validation.php | 2 ++ api/resources/lang/mr/validation.php | 2 ++ api/resources/lang/pa/validation.php | 4 +++- api/resources/lang/pt-BR/validation.php | 2 ++ api/resources/lang/pt/validation.php | 2 ++ api/resources/lang/ru/validation.php | 2 ++ api/resources/lang/ta/validation.php | 2 ++ api/resources/lang/te/validation.php | 2 ++ api/resources/lang/tr/validation.php | 2 ++ api/resources/lang/ur/validation.php | 2 ++ api/resources/lang/vi/validation.php | 2 ++ api/resources/lang/zh-CN/validation.php | 2 ++ api/resources/lang/zh/validation.php | 2 ++ 24 files changed, 51 insertions(+), 7 deletions(-) diff --git a/api/app/Rules/ValidHCaptcha.php b/api/app/Rules/ValidHCaptcha.php index b535690ce..b3ca00071 100644 --- a/api/app/Rules/ValidHCaptcha.php +++ b/api/app/Rules/ValidHCaptcha.php @@ -10,7 +10,7 @@ class ValidHCaptcha implements ImplicitRule { public const H_CAPTCHA_VERIFY_URL = 'https://hcaptcha.com/siteverify'; - private $error = 'Invalid CAPTCHA. Please prove you\'re not a bot.'; + private $error = 'validation.invalid_captcha'; /** * Determine if the validation rule passes. @@ -22,7 +22,7 @@ class ValidHCaptcha implements ImplicitRule public function passes($attribute, $value) { if (empty($value)) { - $this->error = 'Please complete the captcha.'; + $this->error = 'validation.complete_captcha'; return false; } @@ -46,6 +46,6 @@ public function validate(string $attribute, mixed $value, Closure $fail): void */ public function message() { - return $this->error; + return trans($this->error); } } diff --git a/api/app/Rules/ValidReCaptcha.php b/api/app/Rules/ValidReCaptcha.php index 2bd86732a..a889d423e 100644 --- a/api/app/Rules/ValidReCaptcha.php +++ b/api/app/Rules/ValidReCaptcha.php @@ -10,7 +10,7 @@ class ValidReCaptcha implements ImplicitRule { public const RECAPTCHA_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'; - private $error = 'Invalid CAPTCHA. Please prove you\'re not a bot.'; + private $error = 'validation.invalid_captcha'; /** * Determine if the validation rule passes. @@ -22,7 +22,7 @@ class ValidReCaptcha implements ImplicitRule public function passes($attribute, $value) { if (empty($value)) { - $this->error = 'Please complete the captcha.'; + $this->error = 'validation.complete_captcha'; return false; } @@ -46,6 +46,6 @@ public function validate(string $attribute, mixed $value, Closure $fail): void */ public function message() { - return $this->error; + return trans($this->error); } } diff --git a/api/resources/lang/ar/validation.php b/api/resources/lang/ar/validation.php index 1aa91cfec..8082e39ee 100644 --- a/api/resources/lang/ar/validation.php +++ b/api/resources/lang/ar/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'إدخال غير صالح. يرجى التصحيح والمحاولة مرة أخرى.', + 'invalid_captcha' => 'التحقق من صحة الحقل غير صحيح. يرجى التحقق من صحة الحقل والمحاولة مرة أخرى.', + 'complete_captcha' => 'يرجى إكمال التحقق من صحة الحقل.', ]; diff --git a/api/resources/lang/bn/validation.php b/api/resources/lang/bn/validation.php index 8d942bcd6..61adf92ba 100644 --- a/api/resources/lang/bn/validation.php +++ b/api/resources/lang/bn/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'অবৈধ ইনপুট। অনুগ্রহ করে সংশোধন করে আবার চেষ্টা করুন।', + 'invalid_captcha' => 'অবৈধ টিপস। অনুগ্রহ করে টিপস টিপুন।', + 'complete_captcha' => 'অনুগ্রহ করে টিপস টিপুন।', ]; diff --git a/api/resources/lang/de/validation.php b/api/resources/lang/de/validation.php index 7cca30856..43579ea1b 100644 --- a/api/resources/lang/de/validation.php +++ b/api/resources/lang/de/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'Ungültige Eingabe. Bitte korrigieren Sie und versuchen Sie es erneut.', + 'invalid_captcha' => 'Ungültige CAPTCHA. Bitte beweisen Sie, dass Sie kein Bot sind.', + 'complete_captcha' => 'Bitte füllen Sie die CAPTCHA aus.', ]; diff --git a/api/resources/lang/en/validation.php b/api/resources/lang/en/validation.php index 0be387d3d..f6b59ed9a 100644 --- a/api/resources/lang/en/validation.php +++ b/api/resources/lang/en/validation.php @@ -150,5 +150,7 @@ 'attributes' => [], 'invalid_json' => 'Invalid input. Please correct and try again.', + 'invalid_captcha' => 'Invalid CAPTCHA. Please prove you\'re not a bot.', + 'complete_captcha' => 'Please complete the captcha.', ]; diff --git a/api/resources/lang/es/validation.php b/api/resources/lang/es/validation.php index 342e3e26c..ef8dd31aa 100755 --- a/api/resources/lang/es/validation.php +++ b/api/resources/lang/es/validation.php @@ -153,5 +153,7 @@ ], 'invalid_json' => 'Entrada no válida. Por favor, corrija e intente nuevamente.', + 'invalid_captcha' => 'Captcha no válido. Por favor, demuestre que no es un bot.', + 'complete_captcha' => 'Por favor, complete el captcha.', ]; diff --git a/api/resources/lang/fr/validation.php b/api/resources/lang/fr/validation.php index 561d001ed..868464fb4 100644 --- a/api/resources/lang/fr/validation.php +++ b/api/resources/lang/fr/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'Entrée invalide. Veuillez corriger et réessayer.', + 'invalid_captcha' => 'Captcha invalide. Veuillez prouver que vous n\'êtes pas un bot.', + 'complete_captcha' => 'Veuillez compléter le captcha.', ]; diff --git a/api/resources/lang/hi/validation.php b/api/resources/lang/hi/validation.php index f0a575e46..a7322d01e 100644 --- a/api/resources/lang/hi/validation.php +++ b/api/resources/lang/hi/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'अमान्य इनपुट। कृपया सुधारें और पुनः प्रयास करें।', + 'invalid_captcha' => 'अमान्य कैप्चा। कृपया दिखाएं कि आप एक बॉट नहीं हैं।', + 'complete_captcha' => 'कृपया कैप्चा भरें।', ]; diff --git a/api/resources/lang/ja/validation.php b/api/resources/lang/ja/validation.php index 29ebc92d9..bb945111d 100644 --- a/api/resources/lang/ja/validation.php +++ b/api/resources/lang/ja/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => '無効な入力です。修正して再度お試しください。', + 'invalid_captcha' => '無効なCAPTCHAです。ボットではないことを証明してください。', + 'complete_captcha' => 'CAPTCHAを完了してください。', ]; diff --git a/api/resources/lang/jv/validation.php b/api/resources/lang/jv/validation.php index 0a6d62853..a859b10fe 100644 --- a/api/resources/lang/jv/validation.php +++ b/api/resources/lang/jv/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'Input ora valid. Mangga dibenakake lan dicoba maneh.', + 'invalid_captcha' => 'CAPTCHA ora valid. Mangga dibenakake lan dicoba maneh.', + 'complete_captcha' => 'CAPTCHA kudu diisi.', ]; diff --git a/api/resources/lang/ko/validation.php b/api/resources/lang/ko/validation.php index 31166974b..c3117b424 100644 --- a/api/resources/lang/ko/validation.php +++ b/api/resources/lang/ko/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => '잘못된 입력입니다. 수정 후 다시 시도해 주세요.', + 'invalid_captcha' => '잘못된 캡차입니다. 봇이 아님을 증명해 주세요.', + 'complete_captcha' => '캡차를 완료해 주세요.', ]; diff --git a/api/resources/lang/mr/validation.php b/api/resources/lang/mr/validation.php index 115ba5888..6024f8f85 100644 --- a/api/resources/lang/mr/validation.php +++ b/api/resources/lang/mr/validation.php @@ -133,4 +133,6 @@ 'attributes' => [], 'invalid_json' => 'अवैध इनपुट. कृपया सुधारून पुन्हा प्रयत्न करा.', + 'invalid_captcha' => 'अवैध कैप्चा। कृपया कैप्चा टिपला आणि पुन्हा प्रयत्न करा।', + 'complete_captcha' => 'कृपया कैप्चा भरा.', ]; diff --git a/api/resources/lang/pa/validation.php b/api/resources/lang/pa/validation.php index f734589fc..e1c8dcc7c 100644 --- a/api/resources/lang/pa/validation.php +++ b/api/resources/lang/pa/validation.php @@ -115,5 +115,7 @@ 'attributes' => [], - 'invalid_json' => 'ਗਲਤ ਇਨਪੁਟ। ਕਿਰਪਾ ਕਰਕੇ ਸਹੀ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।', + 'invalid_json' => 'ਗਲਤ ਇਨਪੁਟ। ਕਿਰਪਾ ਕਰਕਕੇ ਸਹੀ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।', + 'invalid_captcha' => 'ਗਲਤ ਕੈਪਚਾ। ਕਿਰਪਾ ਕਰੋ ਕੈਪਚਾ ਟੀਪ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।', + 'complete_captcha' => 'ਕਿਰਪਾ ਕਰੋ ਕੈਪਚਾ ਭਰੋ।', ]; diff --git a/api/resources/lang/pt-BR/validation.php b/api/resources/lang/pt-BR/validation.php index 9cc60a28f..1f523cf54 100644 --- a/api/resources/lang/pt-BR/validation.php +++ b/api/resources/lang/pt-BR/validation.php @@ -180,4 +180,6 @@ ], 'invalid_json' => 'Entrada inválida. Por favor, corrija e tente novamente.', + 'invalid_captcha' => 'Captcha inválido. Por favor, demonstre que não é um bot.', + 'complete_captcha' => 'Por favor, complete o captcha.', ]; diff --git a/api/resources/lang/pt/validation.php b/api/resources/lang/pt/validation.php index cfcf6dd13..a03437234 100644 --- a/api/resources/lang/pt/validation.php +++ b/api/resources/lang/pt/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'Entrada inválida. Por favor, corrija e tente novamente.', + 'invalid_captcha' => 'Captcha inválido. Por favor, demonstre que não é um bot.', + 'complete_captcha' => 'Por favor, complete o captcha.', ]; diff --git a/api/resources/lang/ru/validation.php b/api/resources/lang/ru/validation.php index af9a77670..b682c8c9d 100644 --- a/api/resources/lang/ru/validation.php +++ b/api/resources/lang/ru/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'Неверный ввод. Пожалуйста, исправьте и попробуйте снова.', + 'invalid_captcha' => 'Неверный CAPTCHA. Пожалуйста, докажите, что вы не бот.', + 'complete_captcha' => 'Пожалуйста, заполните CAPTCHA.', ]; diff --git a/api/resources/lang/ta/validation.php b/api/resources/lang/ta/validation.php index 41c2bef29..1c44a5c28 100644 --- a/api/resources/lang/ta/validation.php +++ b/api/resources/lang/ta/validation.php @@ -133,4 +133,6 @@ 'attributes' => [], 'invalid_json' => 'தவறான உள்ளீடு. சரிசெய்து மீண்டும் முயற்சிக்கவும்.', + 'invalid_captcha' => 'தவறான கையால் வைத்த முறை. மீண்டும் முயற்சிக்கவும்.', + 'complete_captcha' => 'கையால் வைத்த முறையை நிரப்பவும்.', ]; diff --git a/api/resources/lang/te/validation.php b/api/resources/lang/te/validation.php index 91883c1f8..6032afe69 100644 --- a/api/resources/lang/te/validation.php +++ b/api/resources/lang/te/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'చెల్లని ఇన్‌పుట్. దయచేసి సరిచేసి మళ్లీ ప్రయత్నించండి.', + 'invalid_captcha' => 'అవైధ క్యాప్చా. దయచేసి క్యాప్చా టైప్ మరియు మళ్లీ ప్రయత్నించండి.', + 'complete_captcha' => 'దయచేసి క్యాప్చా భరించండి.', ]; diff --git a/api/resources/lang/tr/validation.php b/api/resources/lang/tr/validation.php index 3b77d4e24..bde699497 100644 --- a/api/resources/lang/tr/validation.php +++ b/api/resources/lang/tr/validation.php @@ -133,4 +133,6 @@ 'attributes' => [], 'invalid_json' => 'Geçersiz girdi. Lütfen düzeltin ve tekrar deneyin.', + 'invalid_captcha' => 'Geçersiz CAPTCHA. Lütfen bot olmadığınızı gösterin.', + 'complete_captcha' => 'Lütfen CAPTCHA\'yı tamamlayın.', ]; diff --git a/api/resources/lang/ur/validation.php b/api/resources/lang/ur/validation.php index 87fe5ad97..f9c282387 100644 --- a/api/resources/lang/ur/validation.php +++ b/api/resources/lang/ur/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'غلط ان پٹ۔ براہ کرم درست کریں اور دوبارہ کوشش کریں۔', + 'invalid_captcha' => 'غلط کپچا۔ براہ کرم کپچا ٹائپ کریں اور دوبارہ کوشش کریں۔', + 'complete_captcha' => 'براہ کرم کپچا پر کام کریں۔', ]; diff --git a/api/resources/lang/vi/validation.php b/api/resources/lang/vi/validation.php index 05c2ada83..df40ee2c2 100644 --- a/api/resources/lang/vi/validation.php +++ b/api/resources/lang/vi/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => 'Dữ liệu không hợp lệ. Vui lòng sửa và thử lại.', + 'invalid_captcha' => 'Mã bảo vệ không đúng. Vui lòng nhập lại và thử lại.', + 'complete_captcha' => 'Vui lòng nhập mã bảo vệ.', ]; diff --git a/api/resources/lang/zh-CN/validation.php b/api/resources/lang/zh-CN/validation.php index f5e16a1b8..cd49c9978 100644 --- a/api/resources/lang/zh-CN/validation.php +++ b/api/resources/lang/zh-CN/validation.php @@ -96,4 +96,6 @@ 'attributes' => [], 'invalid_json' => '输入无效。请修正后重试。', + 'invalid_captcha' => '验证码错误。请重新输入并重试。', + 'complete_captcha' => '请完成验证码。', ]; diff --git a/api/resources/lang/zh/validation.php b/api/resources/lang/zh/validation.php index 40934721a..5814a68c9 100644 --- a/api/resources/lang/zh/validation.php +++ b/api/resources/lang/zh/validation.php @@ -116,4 +116,6 @@ 'attributes' => [], 'invalid_json' => '输入无效。请修正后重试。', + 'invalid_captcha' => '验证码错误。请重新输入并重试。', + 'complete_captcha' => '请完成验证码。', ]; From c649d8c997b9e00f5ea1633d2471aebce510c7ca Mon Sep 17 00:00:00 2001 From: Chirag Chhatrala Date: Wed, 18 Dec 2024 18:01:48 +0530 Subject: [PATCH 6/8] Refactor registration process to integrate reCAPTCHA - Replaced hCaptcha implementation with reCAPTCHA in RegisterController and related test cases. - Updated validation rules to utilize g-recaptcha-response instead of h-captcha-response. - Modified RegisterForm component to support reCAPTCHA, including changes to the form data structure and component references. - Enhanced test cases to reflect the new reCAPTCHA integration, ensuring proper validation and response handling. These changes improve security and user experience during the registration process by adopting a more widely used captcha solution. --- .../Controllers/Auth/RegisterController.php | 6 ++-- api/tests/Feature/RegisterTest.php | 18 +++++----- .../pages/auth/components/RegisterForm.vue | 34 +++++++------------ 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/api/app/Http/Controllers/Auth/RegisterController.php b/api/app/Http/Controllers/Auth/RegisterController.php index 447689a1c..3870754d5 100644 --- a/api/app/Http/Controllers/Auth/RegisterController.php +++ b/api/app/Http/Controllers/Auth/RegisterController.php @@ -12,7 +12,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; -use App\Rules\ValidHCaptcha; +use App\Rules\ValidReCaptcha; class RegisterController extends Controller { @@ -71,8 +71,8 @@ protected function validator(array $data) 'utm_data' => ['nullable', 'array'], ]; - if (config('services.h_captcha.secret_key')) { - $rules['h-captcha-response'] = [new ValidHCaptcha()]; + if (config('services.recaptcha.secret_key')) { + $rules['g-recaptcha-response'] = [new ValidReCaptcha()]; } return Validator::make($data, $rules, [ diff --git a/api/tests/Feature/RegisterTest.php b/api/tests/Feature/RegisterTest.php index 598374cd4..ed5aa538e 100644 --- a/api/tests/Feature/RegisterTest.php +++ b/api/tests/Feature/RegisterTest.php @@ -1,13 +1,13 @@ Http::response(['success' => true]) + ValidReCaptcha::RECAPTCHA_VERIFY_URL => Http::response(['success' => true]) ]); $this->postJson('/register', [ @@ -17,7 +17,7 @@ 'password' => 'secret', 'password_confirmation' => 'secret', 'agree_terms' => true, - 'h-captcha-response' => 'test-token', // Mock token for testing + 'g-recaptcha-response' => 'test-token', // Mock token for testing ]) ->assertSuccessful() ->assertJsonStructure(['id', 'name', 'email']); @@ -36,7 +36,7 @@ 'email' => 'test@test.app', 'password' => 'secret', 'password_confirmation' => 'secret', - 'h-captcha-response' => 'test-token', + 'g-recaptcha-response' => 'test-token', ]) ->assertStatus(422) ->assertJsonValidationErrors(['email']); @@ -44,7 +44,7 @@ it('cannot register with disposable email', function () { Http::fake([ - ValidHCaptcha::H_CAPTCHA_VERIFY_URL => Http::response(['success' => true]) + ValidReCaptcha::RECAPTCHA_VERIFY_URL => Http::response(['success' => true]) ]); // Select random email @@ -62,7 +62,7 @@ 'password' => 'secret', 'password_confirmation' => 'secret', 'agree_terms' => true, - 'h-captcha-response' => 'test-token', + 'g-recaptcha-response' => 'test-token', ]) ->assertStatus(422) ->assertJsonValidationErrors(['email']) @@ -77,10 +77,10 @@ }); it('requires hcaptcha token in production', function () { - config(['services.h_captcha.secret_key' => 'test-key']); + config(['services.recaptcha.secret_key' => 'test-key']); Http::fake([ - ValidHCaptcha::H_CAPTCHA_VERIFY_URL => Http::response(['success' => true]) + ValidReCaptcha::RECAPTCHA_VERIFY_URL => Http::response(['success' => true]) ]); $this->postJson('/register', [ @@ -92,5 +92,5 @@ 'agree_terms' => true, ]) ->assertStatus(422) - ->assertJsonValidationErrors(['h-captcha-response']); + ->assertJsonValidationErrors(['g-recaptcha-response']); }); diff --git a/client/components/pages/auth/components/RegisterForm.vue b/client/components/pages/auth/components/RegisterForm.vue index 01dd3538c..fdf0b8dc7 100644 --- a/client/components/pages/auth/components/RegisterForm.vue +++ b/client/components/pages/auth/components/RegisterForm.vue @@ -52,18 +52,16 @@ label="Confirm Password" /> - +
- -
@@ -141,11 +139,10 @@