From 510c4146276b15659f73f4c8d3a6f0c8d62c645e Mon Sep 17 00:00:00 2001 From: Gideon Okuro Date: Wed, 1 Nov 2023 21:02:28 +0300 Subject: [PATCH] login: restrict diacritic characters (fixes #2659) (#2661) Co-authored-by: dogi --- app/build.gradle | 4 +- .../myplanet/ui/sync/LoginActivity.java | 51 +++++++++++--- .../ui/userprofile/BecomeMemberActivity.kt | 68 +++++++++++++------ app/src/main/res/values-ar/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values-ne/strings.xml | 2 +- app/src/main/res/values-so/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 9 files changed, 100 insertions(+), 35 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d9054d6b19..119d18a5fa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "org.ole.planet.myplanet" minSdkVersion 21 targetSdkVersion 34 - versionCode 1129 - versionName "0.11.29" + versionCode 1130 + versionName "0.11.30" ndkVersion '21.3.6528147' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/sync/LoginActivity.java b/app/src/main/java/org/ole/planet/myplanet/ui/sync/LoginActivity.java index d5837a15c4..6f06ab8f8d 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/sync/LoginActivity.java +++ b/app/src/main/java/org/ole/planet/myplanet/ui/sync/LoginActivity.java @@ -7,6 +7,7 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.graphics.drawable.AnimationDrawable; +import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.Editable; @@ -66,6 +67,7 @@ import org.ole.planet.myplanet.utilities.SharedPrefManager; import org.ole.planet.myplanet.utilities.Utilities; +import java.text.Normalizer; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -73,6 +75,8 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import io.realm.Realm; @@ -338,25 +342,43 @@ private void showGuestLoginDialog() { alertGuestLoginBinding.etUserName.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - char firstChar = s.length() > 0 ? s.charAt(0) : '\0'; + String input = s.toString(); + char firstChar = input.length() > 0 ? input.charAt(0) : '\0'; boolean hasInvalidCharacters = false; - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); + boolean hasSpecialCharacters = false; + boolean hasDiacriticCharacters = false; + + String normalizedText = Normalizer.normalize(s, Normalizer.Form.NFD); + + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); if (c != '_' && c != '.' && c != '-' && !Character.isDigit(c) && !Character.isLetter(c)) { hasInvalidCharacters = true; break; } } + String regex = ".*[ßäöüéèêæÆœøØ¿àìòùÀÈÌÒÙáíóúýÁÉÍÓÚÝâîôûÂÊÎÔÛãñõÃÑÕëïÿÄËÏÖÜŸåÅŒçÇðÐ].*"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(input); + hasSpecialCharacters = matcher.matches(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + hasDiacriticCharacters = !normalizedText.codePoints().allMatch( + codePoint -> Character.isLetterOrDigit(codePoint) || codePoint == '.' || codePoint == '-' || codePoint == '_' + ); + } + if (!Character.isDigit(firstChar) && !Character.isLetter(firstChar)) { alertGuestLoginBinding.etUserName.setError(getString(R.string.must_start_with_letter_or_number)); - } else if (hasInvalidCharacters) { + } else if (hasInvalidCharacters || hasDiacriticCharacters || hasSpecialCharacters) { alertGuestLoginBinding.etUserName.setError(getString(R.string.only_letters_numbers_and_are_allowed)); } else { - String lowercaseText = s.toString().toLowerCase(Locale.ROOT); - if (!s.toString().equals(lowercaseText)) { + String lowercaseText = input.toLowerCase(Locale.ROOT); + if (!input.equals(lowercaseText)) { alertGuestLoginBinding.etUserName.setText(lowercaseText); alertGuestLoginBinding.etUserName.setSelection(lowercaseText.length()); } @@ -387,8 +409,14 @@ public void afterTextChanged(Editable s) {} String username = alertGuestLoginBinding.etUserName.getText().toString().trim(); Character firstChar = username.isEmpty() ? null : username.charAt(0); boolean hasInvalidCharacters = false; - + boolean hasDiacriticCharacters = false; + boolean hasSpecialCharacters = false; boolean isValid = true; + String normalizedText = Normalizer.normalize(username, Normalizer.Form.NFD); + + String regex = ".*[ßäöüéèêæÆœøØ¿àìòùÀÈÌÒÙáíóúýÁÉÍÓÚÝâîôûÂÊÎÔÛãñõÃÑÕëïÿÄËÏÖÜŸåÅŒçÇðÐ].*"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(username); if (TextUtils.isEmpty(username)) { alertGuestLoginBinding.etUserName.setError(getString(R.string.username_cannot_be_empty)); @@ -404,9 +432,16 @@ public void afterTextChanged(Editable s) {} hasInvalidCharacters = true; break; } + + hasSpecialCharacters = matcher.matches(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + hasDiacriticCharacters = !normalizedText.codePoints().allMatch( + codePoint -> Character.isLetterOrDigit(codePoint) || codePoint == '.' || codePoint == '-' || codePoint == '_' + ); + } } - if (hasInvalidCharacters) { + if (hasInvalidCharacters || hasDiacriticCharacters || hasSpecialCharacters) { alertGuestLoginBinding.etUserName.setError(getString(R.string.only_letters_numbers_and_are_allowed)); isValid = false; } diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/userprofile/BecomeMemberActivity.kt b/app/src/main/java/org/ole/planet/myplanet/ui/userprofile/BecomeMemberActivity.kt index 8376b1ce06..4b7104a4a8 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/userprofile/BecomeMemberActivity.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/userprofile/BecomeMemberActivity.kt @@ -4,13 +4,13 @@ import android.app.DatePickerDialog import android.content.Context import android.content.Intent import android.content.SharedPreferences +import android.os.Build import android.os.Bundle import android.text.Editable import android.text.TextUtils import android.text.TextWatcher import android.view.View import android.widget.ArrayAdapter -import android.widget.RadioButton import com.google.gson.JsonArray import com.google.gson.JsonObject import io.realm.Realm @@ -27,8 +27,10 @@ import org.ole.planet.myplanet.ui.sync.SyncActivity import org.ole.planet.myplanet.utilities.NetworkUtils import org.ole.planet.myplanet.utilities.Utilities import org.ole.planet.myplanet.utilities.VersionUtils +import java.text.Normalizer import java.util.Calendar import java.util.Locale +import java.util.regex.Pattern class BecomeMemberActivity : BaseActivity() { private lateinit var activityBecomeMemberBinding: ActivityBecomeMemberBinding @@ -76,26 +78,45 @@ class BecomeMemberActivity : BaseActivity() { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - s?.let { - val firstChar = if (it.isNotEmpty()) it[0] else null + val input = s.toString() - val hasInvalidCharacters = it.any { char -> - char != '_' && char != '.' && char != '-' && - !Character.isDigit(char) && !Character.isLetter(char) + val firstChar = if (input.isNotEmpty()) input[0] else '\u0000' + var hasInvalidCharacters = false + var hasSpecialCharacters = false + var hasDiacriticCharacters = false + + val normalizedText = Normalizer.normalize(s, Normalizer.Form.NFD) + + for (element in input) { + if (element != '_' && element != '.' && element != '-' + && !Character.isDigit(element) && !Character.isLetter(element)) { + hasInvalidCharacters = true + break + } + } + + val regex = ".*[ßäöüéèêæÆœøØ¿àìòùÀÈÌÒÙáíóúýÁÉÍÓÚÝâîôûÂÊÎÔÛãñõÃÑÕëïÿÄËÏÖÜŸåÅŒçÇðÐ].*" + val pattern = Pattern.compile(regex) + val matcher = pattern.matcher(input) + + hasSpecialCharacters = matcher.matches() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + hasDiacriticCharacters = !normalizedText.codePoints().allMatch { codePoint: Int -> + Character.isLetterOrDigit(codePoint) || codePoint == '.'.code || codePoint == '-'.code || codePoint == '_'.code } + } - if (firstChar != null && !Character.isDigit(firstChar) && !Character.isLetter(firstChar)) { - activityBecomeMemberBinding.etUsername.error = getString(R.string.must_start_with_letter_or_number) - } else if (hasInvalidCharacters) { + if (!Character.isDigit(firstChar) && !Character.isLetter(firstChar)) { + activityBecomeMemberBinding.etUsername.error = getString(R.string.must_start_with_letter_or_number) + } else if (hasInvalidCharacters || hasDiacriticCharacters || hasSpecialCharacters) { activityBecomeMemberBinding.etUsername.error = getString(R.string.only_letters_numbers_and_are_allowed) - } else { - val lowercaseText = it.toString().toLowerCase(Locale.ROOT) - if (it.toString() != lowercaseText) { - activityBecomeMemberBinding.etUsername.setText(lowercaseText) - activityBecomeMemberBinding.etUsername.setSelection(lowercaseText.length) - } - activityBecomeMemberBinding.etUsername.error = null + } else { + val lowercaseText = input.lowercase() + if (input != lowercaseText) { + activityBecomeMemberBinding.etUsername.setText(lowercaseText) + activityBecomeMemberBinding.etUsername.setSelection(lowercaseText.length) } + activityBecomeMemberBinding.etUsername.error = null } } @@ -122,10 +143,19 @@ class BecomeMemberActivity : BaseActivity() { val firstChar = if (username!!.isNotEmpty()) username[0] else null val hasInvalidCharacters = username.any { char -> - char != '_' && char != '.' && char != '-' && - !Character.isDigit(char) && !Character.isLetter(char) + char != '_' && char != '.' && char != '-' && !Character.isDigit(char) && !Character.isLetter(char) } + val normalizedText = Normalizer.normalize(username, Normalizer.Form.NFD) + + val regex = ".*[ßäöüéèêæÆœøØ¿àìòùÀÈÌÒÙáíóúýÁÉÍÓÚÝâîôûÂÊÎÔÛãñõÃÑÕëïÿÄËÏÖÜŸåÅŒçÇðÐ].*" + val pattern = Pattern.compile(regex) + val matcher = pattern.matcher(username) + + val hasSpecialCharacters = matcher.matches() + val hasDiacriticCharacters = !normalizedText.codePoints().allMatch { codePoint: Int -> + Character.isLetterOrDigit(codePoint) || codePoint == '.'.code || codePoint == '-'.code || codePoint == '_'.code + } if (TextUtils.isEmpty(username)) { activityBecomeMemberBinding.etUsername.error = getString(R.string.please_enter_a_username) @@ -133,7 +163,7 @@ class BecomeMemberActivity : BaseActivity() { activityBecomeMemberBinding.etUsername.error = getString(R.string.invalid_username) } else if (firstChar != null && !Character.isDigit(firstChar) && !Character.isLetter(firstChar)) { activityBecomeMemberBinding.etUsername.error = getString(R.string.must_start_with_letter_or_number) - } else if (hasInvalidCharacters) { + } else if (hasInvalidCharacters || hasSpecialCharacters || hasDiacriticCharacters) { activityBecomeMemberBinding.etUsername.error = getString(R.string.only_letters_numbers_and_are_allowed) } else if (TextUtils.isEmpty(password)) { activityBecomeMemberBinding.etPassword.error = getString(R.string.please_enter_a_password) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 5f5b43704a..8a207b26b2 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -941,7 +941,7 @@ AI Chat يرجى التقييم يجب أن تبدأ بحرف أو رقم - يُسمح فقط بالحروف والأرقام و "_" و "." و "-" + فقط الأحرف من a إلى z والأرقام و "_" و "." و "-" مسموح بها إلغاء تحديد الكل تحديد الكل الطلب فشل، يرجى إعادة المحاولة diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 0594884956..b6e5c4bdfb 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -946,7 +946,7 @@ AI Chat Por favor, dé una calificación Debe comenzar con una letra o número - Solo se permiten letras, números, "_" , ".", y "-" + Solo se permiten letras de la a a la z, números y "_", ".", y "-" Deseleccionar todo Seleccionar todo La solicitud ha fallado, por favor inténtelo de nuevo diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 056d1977db..176e467206 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -946,7 +946,7 @@ AI Chat Veuillez donner une note Doit commencer par une lettre ou un chiffre - Seules les lettres, les chiffres, "_" , ".", et "-" sont autorisés + Seules les lettres de a à z, les chiffres, le "_", ".", et "-" sont autorisés Désélectionner tout Sélectionner tout La demande a échoué, veuillez réessayer diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml index 765c09ed5a..49392df9bd 100644 --- a/app/src/main/res/values-ne/strings.xml +++ b/app/src/main/res/values-ne/strings.xml @@ -946,7 +946,7 @@ AI Chat कृपया मूल्याङ्कन दिनुहोस् पात्र वा नम्बरसँग सुरु गर्नु पर्दछ - केवल अक्षर, नम्बर, "_" , ".", र "-" पाएको छ + केवल अक्षरहरू a- z, नम्बर र "_" , "." , "-" पार्स्याद छ सबै चयन गर्दिन सबै चयन गर्नुहोस् अनुरोध असफल भयो, कृपया पुन: प्रयास गर्नुहोस् diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index 34cdf8c6f4..2a8eb6dc73 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -935,7 +935,7 @@ AI Chat si naxariis leh u bixi rating Waa in la bilaabo lambarka ama tirada - Waxaad ka ogolaan kartaa lambarka, tirada, "_", ".", iyo "-" + Waa kaliya luuqadaha a-z, lambarka, "_", ".", iyo "-" ayaa la oggolaaday Dhammaad Maareey Dhammaad Dhammaadee Codka lama yimaado, fadlan dib ugu ciyaar diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 848110f643..0e77f2c41f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1035,7 +1035,7 @@ AI Chat Kindly give a rating Must start with letter or number - Only letters, numbers and "_" , ".", "-" are allowed + Only letters a-z, numbers and "_" , ".", "-" are allowed Config not available. Unselect All Select All