From d30627ff6c52a7cef5b9c215e665d2e738c57929 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 14 Nov 2024 23:32:32 +0100 Subject: [PATCH] fix: frontend sending empty fields or unmodified ones --- backend/api/views/users.py | 15 ++++++++++++--- .../components/Settings/Security/Security.js | 17 ++++++++--------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/backend/api/views/users.py b/backend/api/views/users.py index defb5f18..74886d6c 100644 --- a/backend/api/views/users.py +++ b/backend/api/views/users.py @@ -13,7 +13,7 @@ from rest_framework_simplejwt.token_blacklist.models import OutstandingToken, BlacklistedToken -from django.contrib.auth.hashers import make_password +from django.contrib.auth.hashers import make_password, check_password from django.shortcuts import get_object_or_404 from django.db import models, transaction from django.db.models import Count @@ -49,7 +49,7 @@ def patch(self, request, *args, **kwargs): updated_fields = {} for field in allowed_fields: - if field in data: + if field in data and data[field] != '': if field == 'username' and me.oauthAccountID and data[field] != me.username: return Response({"error": "Cannot change username for OAuth accounts."}, status=status.HTTP_400_BAD_REQUEST) elif field == 'username' and data[field] != me.username: @@ -78,7 +78,16 @@ def patch(self, request, *args, **kwargs): updated_fields[field] = data[field] sensitive_fields = ['password', 'phone_number', 'email'] - if any(field in updated_fields for field in sensitive_fields) and me.mfaToken: + changed_sensitive_fields = [field for field in sensitive_fields if field in updated_fields] + + for field in changed_sensitive_fields: + if field == 'password': + if not check_password(updated_fields[field], me.password): + changed_sensitive_fields.remove(field) + elif getattr(me, field) == updated_fields[field]: + changed_sensitive_fields.remove(field) + + if changed_sensitive_fields and me.mfaToken: otp = data.get('otp') if not otp: return Response({"error": "OTP is required to change sensitive information when MFA is enabled."}, status=status.HTTP_400_BAD_REQUEST) diff --git a/frontend/src/components/Settings/Security/Security.js b/frontend/src/components/Settings/Security/Security.js index 35c4e680..f499120c 100644 --- a/frontend/src/components/Settings/Security/Security.js +++ b/frontend/src/components/Settings/Security/Security.js @@ -53,6 +53,7 @@ const Security = ({ user, setUser }) => { ...data, [id]: value, })); + }, []); const checkSecurityRestrictions = useCallback((data, cfPassword) => { @@ -70,11 +71,9 @@ const Security = ({ user, setUser }) => { return t('restrictions.password.missingSpecial'); } else if (password && password !== cfPassword) { return t('restrictions.password.noMatch'); - } else if (!email) { - return t('restrictions.email.required'); - } else if (email.length > 64) { + } else if (email && email.length > 64) { return t('restrictions.email.invalidLength'); - } else if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) { + } else if (email && !/^[^@]+@[^@]+\.[^@]+$/.test(email)) { return t('restrictions.email.invalidFormat'); } else if (phone_number && !/^\+[1-9]\d{1,14}$/.test(phone_number)) { return t('restrictions.phoneNumber.invalidFormat'); @@ -87,11 +86,11 @@ const Security = ({ user, setUser }) => { e.preventDefault(); if (loading) return; - const submissionData = { ...formData }; + const submissionData = {}; ['password', 'phone_number', 'email'].forEach(field => { - if (!submissionData[field]) { - delete submissionData[field]; + if (formData[field] && formData[field] !== user[field] && formData[field].trim() !== '') { + submissionData[field] = formData[field]; } }); @@ -99,9 +98,9 @@ const Security = ({ user, setUser }) => { if (errorMessage) { setError(errorMessage); - } else if ((submissionData.password || submissionData.email || submissionData.phone_number) && has2FA) { + } else if (Object.keys(submissionData).length > 0 && has2FA) { setShowTwoFactorAuth(true); - } else { + } else if (Object.keys(submissionData).length > 0) { setLoading(true); API.patch('users/@me/profile', submissionData) .then(() => {