From 57617dbba43e320ed57a658645bb3ec60f21ca82 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Thu, 12 Sep 2024 16:01:50 +0300 Subject: [PATCH 01/24] Updated settings and env for recaptcha setup usage --- BackEnd/forum/settings.py | 3 +++ sample.env | 2 ++ 2 files changed, 5 insertions(+) diff --git a/BackEnd/forum/settings.py b/BackEnd/forum/settings.py index 77369e081..8b485aceb 100644 --- a/BackEnd/forum/settings.py +++ b/BackEnd/forum/settings.py @@ -268,3 +268,6 @@ def show_toolbar(request): }, }, } + +# ReCaptcha +RECAPTCHA_PRIVATE_KEY = config("RECAPTCHA_PRIVATE_KEY") diff --git a/sample.env b/sample.env index 386cec112..30adbbade 100644 --- a/sample.env +++ b/sample.env @@ -28,3 +28,5 @@ POSTGRES_DB= EMAIL_USE_TLS= REDIS_URL= redis://localhost:6379/0 + +RECAPTCHA_PRIVATE_KEY = From c9076ac828e842ccf4beff3eb60fa71ce82c2abd Mon Sep 17 00:00:00 2001 From: superolegatron Date: Thu, 12 Sep 2024 16:02:34 +0300 Subject: [PATCH 02/24] created function to verify recaptcha from server side --- BackEnd/utils/recaptcha.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 BackEnd/utils/recaptcha.py diff --git a/BackEnd/utils/recaptcha.py b/BackEnd/utils/recaptcha.py new file mode 100644 index 000000000..cecab9dbd --- /dev/null +++ b/BackEnd/utils/recaptcha.py @@ -0,0 +1,16 @@ +from django.conf import settings +import requests + + +def verify_recaptcha(token): + """ + Validates the reCAPTCHA token with Google's API. + """ + recaptcha_url = "https://www.google.com/recaptcha/api/siteverify" + recaptcha_data = { + "secret": settings.RECAPTCHA_PRIVATE_KEY, + "response": token, + } + response = requests.post(recaptcha_url, data=recaptcha_data) + result = response.json() + return result.get("success", False) From a20012ab78f4b57302d7b40ac6ea714c83974a0e Mon Sep 17 00:00:00 2001 From: superolegatron Date: Thu, 12 Sep 2024 16:06:10 +0300 Subject: [PATCH 03/24] Updated signup and signin serializers to validate recaptcha --- BackEnd/authentication/serializers.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/BackEnd/authentication/serializers.py b/BackEnd/authentication/serializers.py index 3aa6814be..60223dbb4 100644 --- a/BackEnd/authentication/serializers.py +++ b/BackEnd/authentication/serializers.py @@ -21,6 +21,8 @@ ) from validation.validate_profile import validate_profile +from utils.recaptcha import verify_recaptcha + User = get_user_model() @@ -42,13 +44,23 @@ class UserRegistrationSerializer(UserCreatePasswordRetypeSerializer): password = serializers.CharField( style={"input_type": "password"}, write_only=True ) + captcha = serializers.CharField( + write_only=True, allow_blank=True, allow_null=True + ) class Meta(UserCreatePasswordRetypeSerializer.Meta): model = User - fields = ("email", "password", "name", "surname", "company") + fields = ("email", "password", "name", "surname", "company", "captcha") def validate(self, value): custom_errors = defaultdict(list) + captcha_token = value.get("captcha") + + if captcha_token and not verify_recaptcha(captcha_token): + custom_errors["captcha"].append( + "Invalid reCAPTCHA. Please try again." + ) + self.fields.pop("re_password", None) re_password = value.pop("re_password") email = value.get("email").lower() @@ -79,6 +91,7 @@ def validate(self, value): return value def create(self, validated_data): + validated_data.pop("captcha", None) company_data = validated_data.pop("company") user = User.objects.create(**validated_data) user.set_password(validated_data["password"]) @@ -105,7 +118,18 @@ class Meta(UserSerializer.Meta): class CustomTokenCreateSerializer(TokenCreateSerializer): + captcha = serializers.CharField( + write_only=True, allow_blank=True, allow_null=True + ) + def validate(self, attrs): + captcha_token = attrs.get("captcha") + + if captcha_token and not verify_recaptcha(captcha_token): + raise serializers.ValidationError( + "Invalid reCAPTCHA. Please try again." + ) + try: validate_profile(attrs.get("email")) except ValidationError as error: From 70d4531c0c732785f4011cf662e1f89933a459ac Mon Sep 17 00:00:00 2001 From: superolegatron Date: Thu, 12 Sep 2024 16:26:09 +0300 Subject: [PATCH 04/24] updated dependencies and env sample to support recaptcha --- FrontEnd/package-lock.json | 25 +++++++++++++++++++++++++ FrontEnd/package.json | 1 + FrontEnd/sample.env | 4 +++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/FrontEnd/package-lock.json b/FrontEnd/package-lock.json index 5b382f25d..758397d11 100644 --- a/FrontEnd/package-lock.json +++ b/FrontEnd/package-lock.json @@ -22,6 +22,7 @@ "react": "^18.2.0", "react-cookie": "^6.1.0", "react-dom": "^18.2.0", + "react-google-recaptcha": "^3.1.0", "react-hook-form": "^7.45.4", "react-router-dom": "^6.22.3", "react-router-hash-link": "^2.4.3", @@ -17322,6 +17323,18 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, + "node_modules/react-async-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", + "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", + "dependencies": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-cookie": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-6.1.0.tgz", @@ -17394,6 +17407,18 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-google-recaptcha": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz", + "integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==", + "dependencies": { + "prop-types": "^15.5.0", + "react-async-script": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-hook-form": { "version": "7.46.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.46.0.tgz", diff --git a/FrontEnd/package.json b/FrontEnd/package.json index 07b6d1d22..7d3c2ab61 100644 --- a/FrontEnd/package.json +++ b/FrontEnd/package.json @@ -29,6 +29,7 @@ "react-scripts": "^5.0.1", "react-timer-hook": "^3.0.7", "react-toastify": "^9.1.3", + "react-google-recaptcha": "^3.1.0", "swr": "^2.2.2", "validator": "^13.11.0", "web-vitals": "^2.1.4" diff --git a/FrontEnd/sample.env b/FrontEnd/sample.env index 47e459c4d..ffa3fd74c 100644 --- a/FrontEnd/sample.env +++ b/FrontEnd/sample.env @@ -1,2 +1,4 @@ REACT_APP_BASE_API_URL= -REACT_APP_PUBLIC_URL= \ No newline at end of file +REACT_APP_PUBLIC_URL= + +REACT_APP_RECAPTCHA_SITE_KEY= \ No newline at end of file From 4f5b5cf19c1c4fedf6037660a942b891c6d45874 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Thu, 12 Sep 2024 16:27:19 +0300 Subject: [PATCH 05/24] Created loader to use recaptcha only when its needed --- .../components/ReCaptcha/ReCartchaLoader.jsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx diff --git a/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx b/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx new file mode 100644 index 000000000..7451f88e9 --- /dev/null +++ b/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx @@ -0,0 +1,22 @@ +import { useEffect } from 'react'; + +const ReCaptchaLoader = () => { + useEffect(() => { + const script = document.createElement('script'); + script.src = `https://www.google.com/recaptcha/api.js?render=${process.env.REACT_APP_RECAPTCHA_SITE_KEY}`; + script.async = true; + document.body.appendChild(script); + + return () => { + document.body.removeChild(script); + const reCaptchaBadge = document.querySelector('.grecaptcha-badge'); + if (reCaptchaBadge) { + reCaptchaBadge.style.display = 'none'; + } + }; + }, []); + + return null; +}; + +export default ReCaptchaLoader; From 94b3c183ce3dd2255b7a449117836a0e7e3c1ab4 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Thu, 12 Sep 2024 16:28:04 +0300 Subject: [PATCH 06/24] Updated signin and signup to work with recaptcha --- .../signup/signup-form/SignUpFormContent.jsx | 21 +++++++++++++++++-- .../components/authorization/LoginPage.jsx | 19 ++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx index eb4ef7865..1cca67eae 100644 --- a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx +++ b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, Suspense } from 'react'; +import React, {useEffect, useState, Suspense, useRef} from 'react'; import { useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { toast } from 'react-toastify'; @@ -14,12 +14,15 @@ import { NAME_SURNAME_PATTERN, COMPANY_NAME_PATTERN, } from '../../../../../constants/constants'; +import ReCAPTCHA from 'react-google-recaptcha'; +import ReCaptchaLoader from '../../../../ReCaptcha/ReCartchaLoader.jsx'; const RulesModal = React.lazy(() => import('./RulesModal')); export function SignUpFormContentComponent(props) { const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const reCaptchaRef = useRef(); const toggleConfirmPassword = () => { setShowConfirmPassword(!showConfirmPassword); }; @@ -125,13 +128,21 @@ export function SignUpFormContentComponent(props) { } }, [watch('password'), watch('confirmPassword'), setError, clearErrors]); - const onSubmit = () => { + const onSubmit = async () => { + let recaptcha_token = null; + try { + recaptcha_token = await reCaptchaRef.current.executeAsync(); + } catch (error) { + console.warn('reCAPTCHA failed or quota expired:', error); + } + const dataToSend = { email: getValues('email'), password: getValues('password'), re_password: getValues('confirmPassword'), name: getValues('name'), surname: getValues('surname'), + captcha: recaptcha_token, company: { name: getValues('companyName'), is_registered: getValues('representative').indexOf('company') > -1, @@ -163,6 +174,7 @@ export function SignUpFormContentComponent(props) { return (
+
+ Loading...}> {isModalOpen && } diff --git a/FrontEnd/src/components/authorization/LoginPage.jsx b/FrontEnd/src/components/authorization/LoginPage.jsx index ed9c3620d..bc70af4c4 100644 --- a/FrontEnd/src/components/authorization/LoginPage.jsx +++ b/FrontEnd/src/components/authorization/LoginPage.jsx @@ -1,5 +1,5 @@ import { useForm } from 'react-hook-form'; -import { useState, useEffect } from 'react'; +import React, {useState, useEffect, useRef} from 'react'; import { useNavigate, Link } from 'react-router-dom'; import axios from 'axios'; import { useStopwatch } from 'react-timer-hook'; @@ -11,11 +11,14 @@ import EyeInvisible from './EyeInvisible'; import classes from './LoginPage.module.css'; import { useAuth } from '../../hooks/'; import checkIfStaff from '../adminPage/checkIfStaff'; +import ReCAPTCHA from 'react-google-recaptcha'; +import ReCaptchaLoader from '../ReCaptcha/ReCartchaLoader'; const LoginContent = () => { const { login } = useAuth(); const navigate = useNavigate(); const [showPassword, setShowPassword] = useState(false); + const reCaptchaRef = useRef(); const togglePassword = () => { setShowPassword(!showPassword); @@ -81,12 +84,20 @@ const LoginContent = () => { const disabled = !isValid || (isRunning && minutes < 10); const onSubmit = async (value) => { + let recaptcha_token = null; + try { + recaptcha_token = await reCaptchaRef.current.executeAsync(); + } catch (error) { + console.warn('reCAPTCHA failed or quota expired:', error); + } + try { const response = await axios.post( `${process.env.REACT_APP_BASE_API_URL}/api/auth/token/login/`, { email: value.email, password: value.password, + captcha: recaptcha_token, } ); const authToken = response.data.auth_token; @@ -127,6 +138,7 @@ const LoginContent = () => { return (
+

Вхід на платформу

@@ -226,6 +238,11 @@ const LoginContent = () => {
+ ); From b8bdf2b9ec5c6a0ffa5edf3d9e6960af7f04544e Mon Sep 17 00:00:00 2001 From: superolegatron Date: Thu, 12 Sep 2024 16:29:12 +0300 Subject: [PATCH 07/24] Unrelated to task, small fix to avoid error in FE console --- FrontEnd/src/components/profileList/ProfileCard.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/FrontEnd/src/components/profileList/ProfileCard.jsx b/FrontEnd/src/components/profileList/ProfileCard.jsx index a0574dbe6..3573eaeb2 100644 --- a/FrontEnd/src/components/profileList/ProfileCard.jsx +++ b/FrontEnd/src/components/profileList/ProfileCard.jsx @@ -108,7 +108,7 @@ export default function ProfileCard({ isAuthorized, data, savedIsUpdated, onClea
Date: Fri, 13 Sep 2024 00:31:33 +0300 Subject: [PATCH 08/24] small fix --- .github/workflows/tests_be.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests_be.yml b/.github/workflows/tests_be.yml index 4e3a662b9..fe454c478 100644 --- a/.github/workflows/tests_be.yml +++ b/.github/workflows/tests_be.yml @@ -18,6 +18,7 @@ env: CORS_ORIGIN_WHITELIST: '' ALLOWED_ENV_HOST: '' REDIS_URL: ${{ vars.REDIS_URL }} + RECAPTCHA_PRIVATE_KEY: '' jobs: From 1d98b64362b1924eb5603c825604a45a933ef39d Mon Sep 17 00:00:00 2001 From: superolegatron Date: Fri, 13 Sep 2024 19:15:34 +0300 Subject: [PATCH 09/24] fixed tests --- .../authentication/tests/test_user_autologout.py | 7 +++++++ BackEnd/authentication/tests/test_user_login.py | 16 ++++++++++++++++ BackEnd/authentication/tests/test_user_logout.py | 7 +++++++ .../tests/test_user_registration.py | 13 +++++++++++++ .../tests/test_reject_moderation_request.py | 8 ++++++-- 5 files changed, 49 insertions(+), 2 deletions(-) diff --git a/BackEnd/authentication/tests/test_user_autologout.py b/BackEnd/authentication/tests/test_user_autologout.py index edf84e3bf..1a54b3c2a 100644 --- a/BackEnd/authentication/tests/test_user_autologout.py +++ b/BackEnd/authentication/tests/test_user_autologout.py @@ -1,4 +1,5 @@ from datetime import timedelta +from unittest.mock import patch from rest_framework import status from rest_framework.authtoken.models import Token @@ -12,6 +13,10 @@ class UserLogoutAPITests(APITestCase): def setUp(self): + patcher = patch("authentication.serializers.verify_recaptcha", return_value=True) + self.mock_verify_recaptcha = patcher.start() + self.addCleanup(patcher.stop) + self.user = UserFactory( email="test@test.com", name="Test", surname="Test" ) @@ -29,6 +34,7 @@ def test_user_autologout_after_14_days(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ).data["auth_token"] token = Token.objects.get(key=self.test_user_token) @@ -53,6 +59,7 @@ def test_user_autologout_after_10_days(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ).data["auth_token"] token = Token.objects.get(key=self.test_user_token) diff --git a/BackEnd/authentication/tests/test_user_login.py b/BackEnd/authentication/tests/test_user_login.py index d20a70404..b30a1da90 100644 --- a/BackEnd/authentication/tests/test_user_login.py +++ b/BackEnd/authentication/tests/test_user_login.py @@ -1,3 +1,5 @@ +from unittest.mock import patch + from rest_framework import status from rest_framework.test import APITestCase from time import sleep @@ -9,6 +11,10 @@ class UserLoginAPITests(APITestCase): def setUp(self): + patcher = patch("authentication.serializers.verify_recaptcha", return_value=True) + self.mock_verify_recaptcha = patcher.start() + self.addCleanup(patcher.stop) + self.user = UserFactory(email="test@test.com") def test_login_successful(self): @@ -20,6 +26,7 @@ def test_login_successful(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -35,6 +42,7 @@ def test_login_email_incorrect(self): data={ "email": "tost@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -56,6 +64,7 @@ def test_login_password_incorrect(self): data={ "email": "test@test.com", "password": "Test5678", + "captcha": "dummy_captcha", }, ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -77,6 +86,7 @@ def test_login_after_allowed_number_attempts(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ) self.client.post( @@ -84,6 +94,7 @@ def test_login_after_allowed_number_attempts(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ) @@ -92,6 +103,7 @@ def test_login_after_allowed_number_attempts(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ) sleep(6) @@ -110,6 +122,7 @@ def test_login_after_allowed_delay_time(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ) @@ -118,6 +131,7 @@ def test_login_after_allowed_delay_time(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ) self.client.post( @@ -125,6 +139,7 @@ def test_login_after_allowed_delay_time(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ) sleep(6) @@ -133,6 +148,7 @@ def test_login_after_allowed_delay_time(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ) diff --git a/BackEnd/authentication/tests/test_user_logout.py b/BackEnd/authentication/tests/test_user_logout.py index ca6cd66d2..d9493789d 100644 --- a/BackEnd/authentication/tests/test_user_logout.py +++ b/BackEnd/authentication/tests/test_user_logout.py @@ -1,3 +1,5 @@ +from unittest.mock import patch + from rest_framework import status from rest_framework.test import APITestCase @@ -8,6 +10,10 @@ class UserLogoutAPITests(APITestCase): def setUp(self): + patcher = patch("authentication.serializers.verify_recaptcha", return_value=True) + self.mock_verify_recaptcha = patcher.start() + self.addCleanup(patcher.stop) + self.user = UserFactory(email="test@test.com") def test_logout_successful(self): @@ -19,6 +25,7 @@ def test_logout_successful(self): data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ).data["auth_token"] self.client.credentials( diff --git a/BackEnd/authentication/tests/test_user_registration.py b/BackEnd/authentication/tests/test_user_registration.py index 444b87c0d..eaf713a66 100644 --- a/BackEnd/authentication/tests/test_user_registration.py +++ b/BackEnd/authentication/tests/test_user_registration.py @@ -1,3 +1,5 @@ +from unittest.mock import patch + from rest_framework import status from rest_framework.test import APITestCase @@ -8,6 +10,10 @@ class UserRegistrationAPITests(APITestCase): def setUp(self): + patcher = patch("authentication.serializers.verify_recaptcha", return_value=True) + self.mock_verify_recaptcha = patcher.start() + self.addCleanup(patcher.stop) + self.user = UserFactory(email="test@test.com") def test_register_user_yurosoba_successful(self): @@ -19,6 +25,7 @@ def test_register_user_yurosoba_successful(self): "re_password": "Test1234", "name": "Jane", "surname": "Smith", + "captcha": "dummy_captcha", "company": { "name": "My Company", "is_registered": True, @@ -49,6 +56,7 @@ def test_register_user_fop_successful(self): "re_password": "Test1234", "name": "Jane", "surname": "Smith", + "captcha": "dummy_captcha", "company": { "name": "My Company", "is_registered": True, @@ -79,6 +87,7 @@ def test_register_user_email_incorrect(self): "re_password": "Test1234", "name": "Jane", "surname": "Smith", + "captcha": "dummy_captcha", "company": { "name": "My Company", "is_registered": True, @@ -103,6 +112,7 @@ def test_register_user_email_exists(self): "re_password": "Test1234", "name": "Test", "surname": "Test", + "captcha": "dummy_captcha", "company": { "name": "Test Company", "is_registered": True, @@ -127,6 +137,7 @@ def test_register_user_password_incorrect(self): "re_password": "tess", "name": "Jane", "surname": "Smith", + "captcha": "dummy_captcha", "company": { "name": "My Company", "is_registered": True, @@ -157,6 +168,7 @@ def test_register_user_who_represent_empty_fields(self): "re_password": "Test1234", "name": "Jane", "surname": "Smith", + "captcha": "dummy_captcha", "company": { "name": "My Company", "is_registered": False, @@ -181,6 +193,7 @@ def test_register_user_who_represent_both_chosen(self): "re_password": "Test1234", "name": "Jane", "surname": "Smith", + "captcha": "dummy_captcha", "company": { "name": "My Company", "is_registered": True, diff --git a/BackEnd/profiles/tests/test_reject_moderation_request.py b/BackEnd/profiles/tests/test_reject_moderation_request.py index bd820fbbd..f36d71353 100644 --- a/BackEnd/profiles/tests/test_reject_moderation_request.py +++ b/BackEnd/profiles/tests/test_reject_moderation_request.py @@ -393,8 +393,9 @@ def test_reject_banner_and_logo_empty_image_fields( mock_schedule.assert_called_once() mock_revoke.assert_not_called() + @patch("authentication.serializers.verify_recaptcha", return_value=True) def test_login_blocked_user_due_to_rejected_request( - self, mock_revoke, mock_schedule + self, mock_revoke, mock_schedule, mock_verify_recaptcha ): # user updates both banner and logo self.user_client.patch( @@ -429,6 +430,7 @@ def test_login_blocked_user_due_to_rejected_request( data={ "email": "test@test.com", "password": "Test1234", + "captcha": "dummy_captcha", }, ) self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) @@ -439,8 +441,9 @@ def test_login_blocked_user_due_to_rejected_request( mock_schedule.assert_called_once() mock_revoke.assert_called_once() + @patch("authentication.serializers.verify_recaptcha", return_value=True) def test_register_blocked_user_due_to_rejected_request( - self, mock_revoke, mock_schedule + self, mock_revoke, mock_schedule, mock_verify_recaptcha ): # user updates both banner and logo self.user_client.patch( @@ -475,6 +478,7 @@ def test_register_blocked_user_due_to_rejected_request( "re_password": "Test1234", "name": "Test", "surname": "Test", + "captcha": "dummy_captcha", "company": { "name": "Test Company", "is_registered": True, From aecad9f68ad853fadb95364e076fc3c135619bcf Mon Sep 17 00:00:00 2001 From: superolegatron Date: Fri, 13 Sep 2024 19:28:31 +0300 Subject: [PATCH 10/24] minor fixes --- BackEnd/authentication/serializers.py | 3 +-- BackEnd/authentication/tests/test_user_autologout.py | 4 +++- BackEnd/authentication/tests/test_user_login.py | 4 +++- BackEnd/authentication/tests/test_user_logout.py | 4 +++- BackEnd/authentication/tests/test_user_registration.py | 4 +++- .../{utils/recaptcha.py => validation/validate_recaptcha.py} | 0 6 files changed, 13 insertions(+), 6 deletions(-) rename BackEnd/{utils/recaptcha.py => validation/validate_recaptcha.py} (100%) diff --git a/BackEnd/authentication/serializers.py b/BackEnd/authentication/serializers.py index 60223dbb4..37508858a 100644 --- a/BackEnd/authentication/serializers.py +++ b/BackEnd/authentication/serializers.py @@ -20,8 +20,7 @@ validate_password_include_symbols, ) from validation.validate_profile import validate_profile - -from utils.recaptcha import verify_recaptcha +from validation.validate_recaptcha import verify_recaptcha User = get_user_model() diff --git a/BackEnd/authentication/tests/test_user_autologout.py b/BackEnd/authentication/tests/test_user_autologout.py index 1a54b3c2a..fe2b75c28 100644 --- a/BackEnd/authentication/tests/test_user_autologout.py +++ b/BackEnd/authentication/tests/test_user_autologout.py @@ -13,7 +13,9 @@ class UserLogoutAPITests(APITestCase): def setUp(self): - patcher = patch("authentication.serializers.verify_recaptcha", return_value=True) + patcher = patch( + "authentication.serializers.verify_recaptcha", return_value=True + ) self.mock_verify_recaptcha = patcher.start() self.addCleanup(patcher.stop) diff --git a/BackEnd/authentication/tests/test_user_login.py b/BackEnd/authentication/tests/test_user_login.py index b30a1da90..8e7c9b0db 100644 --- a/BackEnd/authentication/tests/test_user_login.py +++ b/BackEnd/authentication/tests/test_user_login.py @@ -11,7 +11,9 @@ class UserLoginAPITests(APITestCase): def setUp(self): - patcher = patch("authentication.serializers.verify_recaptcha", return_value=True) + patcher = patch( + "authentication.serializers.verify_recaptcha", return_value=True + ) self.mock_verify_recaptcha = patcher.start() self.addCleanup(patcher.stop) diff --git a/BackEnd/authentication/tests/test_user_logout.py b/BackEnd/authentication/tests/test_user_logout.py index d9493789d..29c7f279c 100644 --- a/BackEnd/authentication/tests/test_user_logout.py +++ b/BackEnd/authentication/tests/test_user_logout.py @@ -10,7 +10,9 @@ class UserLogoutAPITests(APITestCase): def setUp(self): - patcher = patch("authentication.serializers.verify_recaptcha", return_value=True) + patcher = patch( + "authentication.serializers.verify_recaptcha", return_value=True + ) self.mock_verify_recaptcha = patcher.start() self.addCleanup(patcher.stop) diff --git a/BackEnd/authentication/tests/test_user_registration.py b/BackEnd/authentication/tests/test_user_registration.py index eaf713a66..f9ec1e4e6 100644 --- a/BackEnd/authentication/tests/test_user_registration.py +++ b/BackEnd/authentication/tests/test_user_registration.py @@ -10,7 +10,9 @@ class UserRegistrationAPITests(APITestCase): def setUp(self): - patcher = patch("authentication.serializers.verify_recaptcha", return_value=True) + patcher = patch( + "authentication.serializers.verify_recaptcha", return_value=True + ) self.mock_verify_recaptcha = patcher.start() self.addCleanup(patcher.stop) diff --git a/BackEnd/utils/recaptcha.py b/BackEnd/validation/validate_recaptcha.py similarity index 100% rename from BackEnd/utils/recaptcha.py rename to BackEnd/validation/validate_recaptcha.py From 312bc823b5e789177b21b4254a992498ba952ee5 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Sun, 15 Sep 2024 17:14:04 +0300 Subject: [PATCH 11/24] recaptcha versioning --- .github/workflows/tests_be.yml | 2 +- BackEnd/forum/settings.py | 4 ++-- BackEnd/validation/validate_recaptcha.py | 2 +- sample.env | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests_be.yml b/.github/workflows/tests_be.yml index fe454c478..a8c3a7454 100644 --- a/.github/workflows/tests_be.yml +++ b/.github/workflows/tests_be.yml @@ -18,7 +18,7 @@ env: CORS_ORIGIN_WHITELIST: '' ALLOWED_ENV_HOST: '' REDIS_URL: ${{ vars.REDIS_URL }} - RECAPTCHA_PRIVATE_KEY: '' + RECAPTCHA_V2_PRIVATE_KEY: '' jobs: diff --git a/BackEnd/forum/settings.py b/BackEnd/forum/settings.py index 72b1f15c5..71bc3ca9d 100644 --- a/BackEnd/forum/settings.py +++ b/BackEnd/forum/settings.py @@ -267,8 +267,8 @@ def show_toolbar(request): }, } -# ReCaptcha -RECAPTCHA_PRIVATE_KEY = config("RECAPTCHA_PRIVATE_KEY") +# ReCaptcha V2 Invisible +RECAPTCHA_V2_PRIVATE_KEY = config("RECAPTCHA_V2_PRIVATE_KEY") CONTACTS_INFO = { "email": "craft.forum0@gmail.com", diff --git a/BackEnd/validation/validate_recaptcha.py b/BackEnd/validation/validate_recaptcha.py index cecab9dbd..6f0d7a3ce 100644 --- a/BackEnd/validation/validate_recaptcha.py +++ b/BackEnd/validation/validate_recaptcha.py @@ -8,7 +8,7 @@ def verify_recaptcha(token): """ recaptcha_url = "https://www.google.com/recaptcha/api/siteverify" recaptcha_data = { - "secret": settings.RECAPTCHA_PRIVATE_KEY, + "secret": settings.RECAPTCHA_V2_PRIVATE_KEY, "response": token, } response = requests.post(recaptcha_url, data=recaptcha_data) diff --git a/sample.env b/sample.env index 30adbbade..c88a5dcfd 100644 --- a/sample.env +++ b/sample.env @@ -29,4 +29,4 @@ EMAIL_USE_TLS= REDIS_URL= redis://localhost:6379/0 -RECAPTCHA_PRIVATE_KEY = +RECAPTCHA_V2_PRIVATE_KEY = From 099e21bf196d8a169700928ca81223a75dd77ebe Mon Sep 17 00:00:00 2001 From: superolegatron Date: Sun, 15 Sep 2024 17:26:17 +0300 Subject: [PATCH 12/24] recaptcha versioning --- FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx | 2 +- .../SignUp/components/signup/signup-form/SignUpFormContent.jsx | 2 +- FrontEnd/src/components/authorization/LoginPage.jsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx b/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx index 7451f88e9..a475c46d4 100644 --- a/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx +++ b/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx @@ -3,7 +3,7 @@ import { useEffect } from 'react'; const ReCaptchaLoader = () => { useEffect(() => { const script = document.createElement('script'); - script.src = `https://www.google.com/recaptcha/api.js?render=${process.env.REACT_APP_RECAPTCHA_SITE_KEY}`; + script.src = `https://www.google.com/recaptcha/api.js?render=${process.env.REACT_APP_RECAPTCHA_V2_SITE_KEY}`; script.async = true; document.body.appendChild(script); diff --git a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx index 1cca67eae..d9c3350ad 100644 --- a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx +++ b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx @@ -538,7 +538,7 @@ export function SignUpFormContentComponent(props) { diff --git a/FrontEnd/src/components/authorization/LoginPage.jsx b/FrontEnd/src/components/authorization/LoginPage.jsx index bc70af4c4..f88a0a1dc 100644 --- a/FrontEnd/src/components/authorization/LoginPage.jsx +++ b/FrontEnd/src/components/authorization/LoginPage.jsx @@ -240,7 +240,7 @@ const LoginContent = () => { From 3b7bbec9ebc0314d9a44ea8709a9d4b781b648a7 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Sun, 15 Sep 2024 17:26:17 +0300 Subject: [PATCH 13/24] recaptcha versioning --- FrontEnd/sample.env | 2 +- FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx | 2 +- .../SignUp/components/signup/signup-form/SignUpFormContent.jsx | 2 +- FrontEnd/src/components/authorization/LoginPage.jsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/FrontEnd/sample.env b/FrontEnd/sample.env index ffa3fd74c..a92153139 100644 --- a/FrontEnd/sample.env +++ b/FrontEnd/sample.env @@ -1,4 +1,4 @@ REACT_APP_BASE_API_URL= REACT_APP_PUBLIC_URL= -REACT_APP_RECAPTCHA_SITE_KEY= \ No newline at end of file +REACT_APP_RECAPTCHA_V2_SITE_KEY= \ No newline at end of file diff --git a/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx b/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx index 7451f88e9..a475c46d4 100644 --- a/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx +++ b/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx @@ -3,7 +3,7 @@ import { useEffect } from 'react'; const ReCaptchaLoader = () => { useEffect(() => { const script = document.createElement('script'); - script.src = `https://www.google.com/recaptcha/api.js?render=${process.env.REACT_APP_RECAPTCHA_SITE_KEY}`; + script.src = `https://www.google.com/recaptcha/api.js?render=${process.env.REACT_APP_RECAPTCHA_V2_SITE_KEY}`; script.async = true; document.body.appendChild(script); diff --git a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx index 1cca67eae..d9c3350ad 100644 --- a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx +++ b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx @@ -538,7 +538,7 @@ export function SignUpFormContentComponent(props) { diff --git a/FrontEnd/src/components/authorization/LoginPage.jsx b/FrontEnd/src/components/authorization/LoginPage.jsx index bc70af4c4..f88a0a1dc 100644 --- a/FrontEnd/src/components/authorization/LoginPage.jsx +++ b/FrontEnd/src/components/authorization/LoginPage.jsx @@ -240,7 +240,7 @@ const LoginContent = () => { From ee23c79c871f16408d430cc1adc855dbee3907dd Mon Sep 17 00:00:00 2001 From: superolegatron Date: Sun, 15 Sep 2024 17:36:06 +0300 Subject: [PATCH 14/24] minor fixes --- .../SignUp/components/signup/signup-form/SignUpFormContent.jsx | 2 +- FrontEnd/src/components/authorization/LoginPage.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx index d9c3350ad..72b484349 100644 --- a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx +++ b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx @@ -1,4 +1,4 @@ -import React, {useEffect, useState, Suspense, useRef} from 'react'; +import React, { useEffect, useState, Suspense, useRef } from 'react'; import { useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { toast } from 'react-toastify'; diff --git a/FrontEnd/src/components/authorization/LoginPage.jsx b/FrontEnd/src/components/authorization/LoginPage.jsx index f88a0a1dc..b2ef05269 100644 --- a/FrontEnd/src/components/authorization/LoginPage.jsx +++ b/FrontEnd/src/components/authorization/LoginPage.jsx @@ -1,5 +1,5 @@ import { useForm } from 'react-hook-form'; -import React, {useState, useEffect, useRef} from 'react'; +import { useState, useEffect, useRef } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import axios from 'axios'; import { useStopwatch } from 'react-timer-hook'; From aaf91d4c3798c3310450cc283b9159920b0fdb00 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Sun, 15 Sep 2024 21:40:10 +0300 Subject: [PATCH 15/24] moved url for captcha verification to env --- BackEnd/forum/settings.py | 1 + BackEnd/validation/validate_recaptcha.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/BackEnd/forum/settings.py b/BackEnd/forum/settings.py index 71bc3ca9d..a9c668afe 100644 --- a/BackEnd/forum/settings.py +++ b/BackEnd/forum/settings.py @@ -269,6 +269,7 @@ def show_toolbar(request): # ReCaptcha V2 Invisible RECAPTCHA_V2_PRIVATE_KEY = config("RECAPTCHA_V2_PRIVATE_KEY") +RECAPTCHA_URL = config("RECAPTCHA_URL") CONTACTS_INFO = { "email": "craft.forum0@gmail.com", diff --git a/BackEnd/validation/validate_recaptcha.py b/BackEnd/validation/validate_recaptcha.py index 6f0d7a3ce..a7ed82229 100644 --- a/BackEnd/validation/validate_recaptcha.py +++ b/BackEnd/validation/validate_recaptcha.py @@ -6,7 +6,7 @@ def verify_recaptcha(token): """ Validates the reCAPTCHA token with Google's API. """ - recaptcha_url = "https://www.google.com/recaptcha/api/siteverify" + recaptcha_url = settings.RECAPTCHA_URL recaptcha_data = { "secret": settings.RECAPTCHA_V2_PRIVATE_KEY, "response": token, From 9ce9e35104863801858fce5e562df02d775f4bae Mon Sep 17 00:00:00 2001 From: superolegatron Date: Mon, 16 Sep 2024 13:39:12 +0300 Subject: [PATCH 16/24] updated sample.env --- sample.env | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sample.env b/sample.env index c88a5dcfd..f02fe8a4e 100644 --- a/sample.env +++ b/sample.env @@ -29,4 +29,5 @@ EMAIL_USE_TLS= REDIS_URL= redis://localhost:6379/0 -RECAPTCHA_V2_PRIVATE_KEY = +RECAPTCHA_V2_PRIVATE_KEY = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" # this is test key +RECAPTCHA_URL="https://www.google.com/recaptcha/api/siteverify" From b618bdc44d49faac72f92953cbff55609c6a9703 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Mon, 16 Sep 2024 13:55:25 +0300 Subject: [PATCH 17/24] minor updates --- .github/workflows/tests_be.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests_be.yml b/.github/workflows/tests_be.yml index a8c3a7454..4ecc99cd1 100644 --- a/.github/workflows/tests_be.yml +++ b/.github/workflows/tests_be.yml @@ -19,6 +19,7 @@ env: ALLOWED_ENV_HOST: '' REDIS_URL: ${{ vars.REDIS_URL }} RECAPTCHA_V2_PRIVATE_KEY: '' + RECAPTCHA_URL: 'https://www:google:com/recaptcha/api/siteverify' jobs: From 80ef38ebab23932f8c9e920a194dcfd6886c7a23 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Mon, 16 Sep 2024 13:58:38 +0300 Subject: [PATCH 18/24] minor updates --- .github/workflows/tests_be.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests_be.yml b/.github/workflows/tests_be.yml index 4ecc99cd1..d0cca4c2c 100644 --- a/.github/workflows/tests_be.yml +++ b/.github/workflows/tests_be.yml @@ -19,7 +19,7 @@ env: ALLOWED_ENV_HOST: '' REDIS_URL: ${{ vars.REDIS_URL }} RECAPTCHA_V2_PRIVATE_KEY: '' - RECAPTCHA_URL: 'https://www:google:com/recaptcha/api/siteverify' + RECAPTCHA_URL: ${{ vars.RECAPTCHA_URL }} jobs: From a196e69085e207d60fa4f3fa67694bfdaf2db3e5 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Tue, 24 Sep 2024 16:59:42 +0300 Subject: [PATCH 19/24] small fix to properly handle incorrect form submitting with captcha --- BackEnd/authentication/serializers.py | 20 +++++++++---------- .../components/authorization/LoginPage.jsx | 2 ++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/BackEnd/authentication/serializers.py b/BackEnd/authentication/serializers.py index 37508858a..121bdb0cb 100644 --- a/BackEnd/authentication/serializers.py +++ b/BackEnd/authentication/serializers.py @@ -54,12 +54,6 @@ class Meta(UserCreatePasswordRetypeSerializer.Meta): def validate(self, value): custom_errors = defaultdict(list) captcha_token = value.get("captcha") - - if captcha_token and not verify_recaptcha(captcha_token): - custom_errors["captcha"].append( - "Invalid reCAPTCHA. Please try again." - ) - self.fields.pop("re_password", None) re_password = value.pop("re_password") email = value.get("email").lower() @@ -85,6 +79,10 @@ def validate(self, value): custom_errors["password"].append(error.message) if value["password"] != re_password: custom_errors["password"].append("Passwords don't match.") + if captcha_token and not verify_recaptcha(captcha_token): + custom_errors["captcha"].append( + "Invalid reCAPTCHA. Please try again." + ) if custom_errors: raise serializers.ValidationError(custom_errors) return value @@ -124,11 +122,6 @@ class CustomTokenCreateSerializer(TokenCreateSerializer): def validate(self, attrs): captcha_token = attrs.get("captcha") - if captcha_token and not verify_recaptcha(captcha_token): - raise serializers.ValidationError( - "Invalid reCAPTCHA. Please try again." - ) - try: validate_profile(attrs.get("email")) except ValidationError as error: @@ -139,6 +132,11 @@ def validate(self, attrs): except RateLimitException: self.fail("inactive_account") + if captcha_token and not verify_recaptcha(captcha_token): + raise serializers.ValidationError( + "Invalid reCAPTCHA. Please try again." + ) + @RateLimitDecorator( calls=django_settings.ATTEMPTS_FOR_LOGIN, period=django_settings.DELAY_FOR_LOGIN, diff --git a/FrontEnd/src/components/authorization/LoginPage.jsx b/FrontEnd/src/components/authorization/LoginPage.jsx index b2ef05269..affe49025 100644 --- a/FrontEnd/src/components/authorization/LoginPage.jsx +++ b/FrontEnd/src/components/authorization/LoginPage.jsx @@ -131,6 +131,8 @@ const LoginContent = () => { }); } } + } finally { + reCaptchaRef.current.reset(); } }; From f67caaa9936e60787c0f857279b1dd05e9ab433b Mon Sep 17 00:00:00 2001 From: superolegatron Date: Tue, 24 Sep 2024 17:13:49 +0300 Subject: [PATCH 20/24] added captcha token reset for signup --- .../SignUp/components/signup/signup-form/SignUpFormContent.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx index 72b484349..7fb731769 100644 --- a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx +++ b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx @@ -169,6 +169,9 @@ export function SignUpFormContentComponent(props) { if (error.response && error.response.status === 400) { toast.error('Не вдалося зареєструвати користувача, сталася помилка'); } + }) + .finally(()=>{ + reCaptchaRef.current.reset(); }); }; From 1ae63cba0aafa4e846a3094a9db6f25b4e64b8fe Mon Sep 17 00:00:00 2001 From: superolegatron Date: Tue, 24 Sep 2024 17:24:45 +0300 Subject: [PATCH 21/24] adjusted test according to updated verification --- BackEnd/profiles/tests/test_reject_moderation_request.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/BackEnd/profiles/tests/test_reject_moderation_request.py b/BackEnd/profiles/tests/test_reject_moderation_request.py index f36d71353..edae46969 100644 --- a/BackEnd/profiles/tests/test_reject_moderation_request.py +++ b/BackEnd/profiles/tests/test_reject_moderation_request.py @@ -393,9 +393,8 @@ def test_reject_banner_and_logo_empty_image_fields( mock_schedule.assert_called_once() mock_revoke.assert_not_called() - @patch("authentication.serializers.verify_recaptcha", return_value=True) def test_login_blocked_user_due_to_rejected_request( - self, mock_revoke, mock_schedule, mock_verify_recaptcha + self, mock_revoke, mock_schedule ): # user updates both banner and logo self.user_client.patch( From 5eb01be45ec1c5ded736ce5bd440d13ae8c134e6 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Wed, 25 Sep 2024 01:50:14 +0300 Subject: [PATCH 22/24] small fix --- .../SignUp/components/signup/signup-form/SignUpFormContent.jsx | 2 +- FrontEnd/src/components/authorization/LoginPage.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx index 7fb731769..0e0e095de 100644 --- a/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx +++ b/FrontEnd/src/components/SignUp/components/signup/signup-form/SignUpFormContent.jsx @@ -171,7 +171,7 @@ export function SignUpFormContentComponent(props) { } }) .finally(()=>{ - reCaptchaRef.current.reset(); + reCaptchaRef.current?.reset(); }); }; diff --git a/FrontEnd/src/components/authorization/LoginPage.jsx b/FrontEnd/src/components/authorization/LoginPage.jsx index affe49025..02af86ded 100644 --- a/FrontEnd/src/components/authorization/LoginPage.jsx +++ b/FrontEnd/src/components/authorization/LoginPage.jsx @@ -132,7 +132,7 @@ const LoginContent = () => { } } } finally { - reCaptchaRef.current.reset(); + reCaptchaRef.current?.reset(); } }; From 18943c2016f290feefe9a819e1b8f572787f4585 Mon Sep 17 00:00:00 2001 From: superolegatron Date: Fri, 27 Sep 2024 15:39:45 +0300 Subject: [PATCH 23/24] deleted redundant code --- .../components/ReCaptcha/ReCartchaLoader.jsx | 22 ------------------- .../src/pages/Authorization/LoginPage.jsx | 2 -- .../SignUp/SignupForm/SignUpFormContent.jsx | 2 -- 3 files changed, 26 deletions(-) delete mode 100644 FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx diff --git a/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx b/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx deleted file mode 100644 index a475c46d4..000000000 --- a/FrontEnd/src/components/ReCaptcha/ReCartchaLoader.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useEffect } from 'react'; - -const ReCaptchaLoader = () => { - useEffect(() => { - const script = document.createElement('script'); - script.src = `https://www.google.com/recaptcha/api.js?render=${process.env.REACT_APP_RECAPTCHA_V2_SITE_KEY}`; - script.async = true; - document.body.appendChild(script); - - return () => { - document.body.removeChild(script); - const reCaptchaBadge = document.querySelector('.grecaptcha-badge'); - if (reCaptchaBadge) { - reCaptchaBadge.style.display = 'none'; - } - }; - }, []); - - return null; -}; - -export default ReCaptchaLoader; diff --git a/FrontEnd/src/pages/Authorization/LoginPage.jsx b/FrontEnd/src/pages/Authorization/LoginPage.jsx index 3c64346f4..ca91560b6 100644 --- a/FrontEnd/src/pages/Authorization/LoginPage.jsx +++ b/FrontEnd/src/pages/Authorization/LoginPage.jsx @@ -12,7 +12,6 @@ import classes from './LoginPage.module.css'; import { useAuth } from '../../hooks'; import checkIfStaff from '../AdminPage/checkIfStaff'; import ReCAPTCHA from 'react-google-recaptcha'; -import ReCaptchaLoader from '../../components/ReCaptcha/ReCartchaLoader'; const LoginContent = () => { const { login } = useAuth(); @@ -141,7 +140,6 @@ const LoginContent = () => { return (
-

Вхід на платформу

diff --git a/FrontEnd/src/pages/SignUp/SignupForm/SignUpFormContent.jsx b/FrontEnd/src/pages/SignUp/SignupForm/SignUpFormContent.jsx index bb1f85d3f..411a2ffdd 100644 --- a/FrontEnd/src/pages/SignUp/SignupForm/SignUpFormContent.jsx +++ b/FrontEnd/src/pages/SignUp/SignupForm/SignUpFormContent.jsx @@ -10,7 +10,6 @@ import EyeVisible from '../../../pages/Authorization/EyeVisible'; import styles from './SignUpFormContent.module.css'; import PropTypes from 'prop-types'; import ReCAPTCHA from 'react-google-recaptcha'; -import ReCaptchaLoader from '../../../components/ReCaptcha/ReCartchaLoader.jsx'; import { EMAIL_PATTERN, PASSWORD_PATTERN, @@ -173,7 +172,6 @@ export function SignUpFormContentComponent(props) { return (
-
Date: Tue, 1 Oct 2024 19:36:34 +0300 Subject: [PATCH 24/24] fixed uncaught in promise timeout --- FrontEnd/src/pages/SignUp/SignupForm/SignUpFormContent.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FrontEnd/src/pages/SignUp/SignupForm/SignUpFormContent.jsx b/FrontEnd/src/pages/SignUp/SignupForm/SignUpFormContent.jsx index 411a2ffdd..ef137f30a 100644 --- a/FrontEnd/src/pages/SignUp/SignupForm/SignUpFormContent.jsx +++ b/FrontEnd/src/pages/SignUp/SignupForm/SignUpFormContent.jsx @@ -23,6 +23,7 @@ export function SignUpFormContentComponent(props) { const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const reCaptchaRef = useRef(); + const onReCaptchaChange = () => reCaptchaRef.current = !reCaptchaRef.current; const toggleConfirmPassword = () => { setShowConfirmPassword(!showConfirmPassword); }; @@ -164,9 +165,6 @@ export function SignUpFormContentComponent(props) { if (error.response && error.response.status === 400) { toast.error('Не вдалося зареєструвати користувача, сталася помилка'); } - }) - .finally(()=>{ - reCaptchaRef.current?.reset(); }); }; @@ -560,6 +558,7 @@ export function SignUpFormContentComponent(props) { ref={reCaptchaRef} sitekey={process.env.REACT_APP_RECAPTCHA_V2_SITE_KEY} size="invisible" + onChange={onReCaptchaChange} />
Loading...
}>