From 8a8eba612c71414d40900c3204f91fc566eae175 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 16:49:13 -0800 Subject: [PATCH 01/95] set the descriptions for the captions and strings#12881 --- .../main/java/de/symeda/sormas/api/i18n/Captions.java | 5 +++++ .../src/main/java/de/symeda/sormas/api/i18n/Strings.java | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java index d5ef293848c..75357deec80 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java @@ -2622,11 +2622,16 @@ public interface Captions { String User_userEmail = "User.userEmail"; String User_userName = "User.userName"; String User_userRoles = "User.userRoles"; + String updatePassword = "updatePassword"; + String currentPassword = "currentPassword"; + String confirmPassword = "confirmPassword"; + String passwordStrength = "passwordStrength"; String User_uuid = "User.uuid"; String userMyUserId = "userMyUserId"; String userNewUser = "userNewUser"; String userResetPassword = "userResetPassword"; String userRestrictDiseases = "userRestrictDiseases"; + String userGeneratePassword = "userGeneratePassword"; String userRight = "userRight"; String UserRight_caption = "UserRight.caption"; String UserRight_description = "UserRight.description"; diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java index e54f53c6709..0e1ffbb4b35 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java @@ -667,6 +667,7 @@ public interface Strings { String headingLineListingImport = "headingLineListingImport"; String headingLocation = "headingLocation"; String headingLoginFailed = "headingLoginFailed"; + String headingUpdatePasswordFailed = "headingUpdatePasswordFailed"; String headingMaternalHistory = "headingMaternalHistory"; String headingMedicalInformation = "headingMedicalInformation"; String headingMergeDuplicateEventParticipantSamePersonSameEvent = "headingMergeDuplicateEventParticipantSamePersonSameEvent"; @@ -708,6 +709,10 @@ public interface Strings { String headingPaperFormDates = "headingPaperFormDates"; String headingPathogenTestsDeleted = "headingPathogenTestsDeleted"; String headingPersonData = "headingPersonData"; + String currentPassword = "currentPassword"; + String updatePassword = "updatePassword"; + String confirmPassword = "confirmPassword"; + String passwordStrength = "passwordStrength"; String headingPersonInformation = "headingPersonInformation"; String headingPersonOccupation = "headingPersonOccupation"; String headingPickEventGroup = "headingPickEventGroup"; @@ -816,6 +821,7 @@ public interface Strings { String headingUpdatedPersonInformation = "headingUpdatedPersonInformation"; String headingUpdatedSampleInformation = "headingUpdatedSampleInformation"; String headingUpdatePassword = "headingUpdatePassword"; + String headingChangePassword = "headingChangePassword"; String headingUpdatePersonContactDetails = "headingUpdatePersonContactDetails"; String headingUploadSuccess = "headingUploadSuccess"; String headingUserData = "headingUserData"; @@ -1343,6 +1349,9 @@ public interface Strings { String messageLineListingDisabled = "messageLineListingDisabled"; String messageLineListingSaved = "messageLineListingSaved"; String messageLoginFailed = "messageLoginFailed"; + String messageNewPasswordDoesNotMatchFailed = "messageNewPasswordDoesNotMatchFailed"; + String messagePasswordFailed = "messagePasswordFailed"; + String messageNewPasswordFailed = "messageNewPasswordFailed"; String messageMissingCases = "messageMissingCases"; String messageMissingDateFilter = "messageMissingDateFilter"; String messageMissingEpiWeekFilter = "messageMissingEpiWeekFilter"; From 8b0c7ed445d472ebd1a33f6056526deac899d8c0 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 16:52:15 -0800 Subject: [PATCH 02/95] current password, new password, confirm password and password strength#12881 --- .../de/symeda/sormas/api/user/UserDto.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserDto.java index a2e98c0232d..718e928ea53 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserDto.java @@ -68,6 +68,9 @@ public class UserDto extends EntityDto { public static final String LANGUAGE = "language"; public static final String HAS_CONSENTED_TO_GDPR = "hasConsentedToGdpr"; public static final String JURISDICTION_LEVEL = "jurisdictionLevel"; + public static final String PASSWORD = "currentPassword"; + public static final String NEW_PASSWORD = "updatePassword"; + public static final String CONFIRM_PASSWORD = "confirmPassword"; private boolean active = true; @@ -106,6 +109,10 @@ public class UserDto extends EntityDto { private Language language; private boolean hasConsentedToGdpr; + private String currentPassword; + private String updatePassword; + private String confirmPassword; + private String passwordStrength; private JurisdictionLevel jurisdictionLevel; @@ -160,6 +167,38 @@ public void setUserEmail(String userEmail) { this.userEmail = userEmail; } + public void setCurrentPassword(String currentPassword) { + this.currentPassword = currentPassword; + } + + public String getCurrentPassword() { + return currentPassword; + } + + public void setUpdatePassword(String updatePassword) { + this.updatePassword = updatePassword; + } + + public String getUpdatePassword() { + return updatePassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void PasswordStrength(String passwordStrength) { + this.passwordStrength = passwordStrength; + } + + public String getPasswordStrength() { + return passwordStrength; + } + public String getPhone() { return phone; } From 7b2a25a0c8ca3bcfc758d853e487f194f41a92cf Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 16:57:27 -0800 Subject: [PATCH 03/95] #21-Added methods to updatePassword validate current password, check password strength and generate password --- .../java/de/symeda/sormas/api/user/UserFacade.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserFacade.java index c859c4fa0c1..5ce3ebd6724 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserFacade.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserFacade.java @@ -50,6 +50,20 @@ public interface UserFacade { String resetPassword(String uuid); + String updatePassword(String uuid, String password); + + String updateUserPassword(String uuid, String newPassword, String currentPassword); + + List changeUserPassword(String uuid, String password); + + boolean validatePassword(String uuid, String password); + + boolean validatePasswordPattern(String password); + + String checkPasswordStrength(String password); + + String generatePassword(); + List getAllAfter(Date date); UserDto getByUserName(String userName); From a679d4760ac080dd80a7c8ab5ca8c63c6d06db54 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 16:59:12 -0800 Subject: [PATCH 04/95] current password, new password, confirm password and password strength#12881 --- sormas-api/src/main/resources/captions.properties | 6 ++++++ sormas-api/src/main/resources/strings.properties | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/sormas-api/src/main/resources/captions.properties b/sormas-api/src/main/resources/captions.properties index 151ef53d2f0..292c50aa5b1 100644 --- a/sormas-api/src/main/resources/captions.properties +++ b/sormas-api/src/main/resources/captions.properties @@ -2563,6 +2563,7 @@ TreatmentExport.caseName=Case name userNewUser=New user userMyUserId=My user ID userResetPassword=Create new password +userGeneratePassword=Generate new password userUpdatePasswordConfirmation=Really update password? sync=Sync syncUsers=Sync Users @@ -2587,6 +2588,11 @@ User.region=Region User.district=District User.community=Community userRestrictDiseases=Restrict access to specific diseases +currentPassword=Enter Current Password +updatePassword=Enter new Password +confirmPassword=Confirm new Password +passwordStrength=Password strength + # Vaccination vaccinationNewVaccination=New vaccination vaccinationNoVaccinationsForPerson=There are no vaccinations for this person diff --git a/sormas-api/src/main/resources/strings.properties b/sormas-api/src/main/resources/strings.properties index 6188641d36f..0324185911a 100644 --- a/sormas-api/src/main/resources/strings.properties +++ b/sormas-api/src/main/resources/strings.properties @@ -605,6 +605,7 @@ headingLineListing = Line listing headingLineListingImport = Line listing import headingLocation = Location headingLoginFailed = Login failed +headingUpdatePasswordFailed = Update password failed headingMaternalHistory = Maternal history headingMedicalInformation = Additional medical information headingMissingDateFilter = Missing date filter @@ -636,6 +637,10 @@ headingOutbreakIn = Outbreak in headingPathogenTestsDeleted = Pathogen tests deleted headingPaperFormDates = Reception dates of paper form headingPersonData = Person data +currentPassword=Enter Current Password +updatePassword=Enter new Password +confirmPassword=Confirm new Password ++passwordStrength=Password strength headingPersonInformation = Person information headingPersonOccupation = Occupation & education headingPickEventGroup = Pick event group @@ -686,6 +691,7 @@ headingReferCaseFromPointOfEntry = Refer case from point of entry headingTreatments = Executed treatments headingTreatmentsDeleted = Treatments deleted headingUpdatePassword = Update password +headingChangePassword = Change password headingUploadSuccess = Upload Successful headingUserData = User data headingVaccination = Vaccination @@ -1262,6 +1268,9 @@ messageUploadSuccessful = Upload successful! You can now close this window. messageIncompleteGpsCoordinates = GPS coordinates are incomplete messageExternalMessagesAssigned = The assignee has been changed for all selected messages messageLoginFailed = Please check your username and password and try again +messageNewPasswordDoesNotMatchFailed = Please check your new Password and Current Password and try again +messagePasswordFailed = Incorrect Password Please check your Password and try again +messageNewPasswordFailed = Password should contain some text, \n atleast one special character \n some numbers \n and should be more than 8 characters messageMissingCases = Please generate some cases before generating contacts messageMissingDateFilter = Please fill in both date filter fields messageMissingEpiWeekFilter = Please fill in both epi week filter fields From f9e5afbda7ee2d42ca506c1d7fb802f7d70ac400 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 17:06:58 -0800 Subject: [PATCH 05/95] Added plugin to check password strength#12881 --- sormas-app/app/build.gradle | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sormas-app/app/build.gradle b/sormas-app/app/build.gradle index a77d795a1ad..1477bb62e40 100644 --- a/sormas-app/app/build.gradle +++ b/sormas-app/app/build.gradle @@ -74,6 +74,7 @@ android { } buildFeatures { dataBinding true + viewBinding true } // needed for pre androidx testing. This will not make it's way into the apk @@ -94,6 +95,12 @@ repositories { } dependencies { + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0' + def lifecycle_version = "2.2.0" + def paging_version = "3.0.1" + implementation fileTree(include: ['*.jar'], dir: 'libs') implementation platform('com.google.firebase:firebase-bom:32.2.0') implementation 'androidx.appcompat:appcompat:1.6.1' From 056ec34363c007d8d3d1e53aca8dde2c9c72e74f Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 17:07:55 -0800 Subject: [PATCH 06/95] Added activity for changePasswordActivity#12881 --- sormas-app/app/src/main/AndroidManifest.xml | 442 ++++++++++---------- 1 file changed, 212 insertions(+), 230 deletions(-) diff --git a/sormas-app/app/src/main/AndroidManifest.xml b/sormas-app/app/src/main/AndroidManifest.xml index 18d3de04c70..ab9e3b3de28 100644 --- a/sormas-app/app/src/main/AndroidManifest.xml +++ b/sormas-app/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + @@ -10,9 +10,11 @@ + + - + + android:theme="@style/SormasTheme" > + - @@ -31,397 +40,375 @@ android:theme="@style/LoginActivityTheme" android:screenOrientation="portrait" android:exported="true" - android:launchMode="singleTop"> + android:launchMode="singleTop" + android:screenOrientation="portrait" + android:theme="@style/LoginActivityTheme" > + + android:theme="@style/ActivityWithOverflowMenuTheme" > - - - - - + + + android:screenOrientation="portrait" + android:theme="@style/ActivityWithOverflowMenuTheme" + android:windowSoftInputMode="stateHidden" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - - + + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > - - + android:theme="@style/ActivityWithOverflowMenuTheme" > + + + + android:exported="true" > - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - + + + + + + + + android:grantUriPermissions="true" > - + android:name=".core.TaskNotificationService" + android:enabled="true" /> - - - - - - + android:name=".core.FeatureConfigurationService" + android:enabled="true" /> + + + + android:exported="true" > - + + From 68916e53c6e30dc52070ef0eae7a799f1876bf7f Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 17:11:45 -0800 Subject: [PATCH 07/95] #21-Added function to setNewPassword locally to android --- .../sormas/app/backend/config/ConfigProvider.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/backend/config/ConfigProvider.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/backend/config/ConfigProvider.java index ca3d83e329f..5353e75e89f 100644 --- a/sormas-app/app/src/main/java/de/symeda/sormas/app/backend/config/ConfigProvider.java +++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/backend/config/ConfigProvider.java @@ -130,6 +130,8 @@ private ConfigProvider(Context context) { this.context = context; } + + private boolean hasDeviceEncryption() { // Device encryption is no longer used, because it's not reliably implemented @@ -311,6 +313,15 @@ public static void setUsernameAndPassword(String username, String password) { setRepullNeeded(true); } } + public static void setNewPassword(String password) { + if (password == null) + throw new NullPointerException("password"); + password = instance.encodeCredential(password, "Password"); + + instance.password = password; + DatabaseHelper.getConfigDao().createOrUpdate(new Config(KEY_PASSWORD, password)); + + } public static void setPin(String pin) { if (pin == null) { From f0634c46ab730fe793eedad6dd634d73171a9860 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 17:13:11 -0800 Subject: [PATCH 08/95] #21-Added saveNewPassword and generatePassword calls to the server --- .../de/symeda/sormas/app/backend/user/UserDtoHelper.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/backend/user/UserDtoHelper.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/backend/user/UserDtoHelper.java index 647aa000902..bbc8b7a021f 100644 --- a/sormas-app/app/src/main/java/de/symeda/sormas/app/backend/user/UserDtoHelper.java +++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/backend/user/UserDtoHelper.java @@ -132,4 +132,12 @@ public static UserReferenceDto toReferenceDto(User ado) { UserReferenceDto dto = new UserReferenceDto(ado.getUuid()); return dto; } + public static Call saveNewPassword(String uuid,String newPassword,String currentPassword) throws NoConnectionException { + return RetroProvider.getUserFacade().saveNewPassword(uuid,newPassword,currentPassword); + } + public static Call generatePassword() throws NoConnectionException { + return RetroProvider.getUserFacade().generatePassword(); + } + + } From 9709c8679174192bd4b415e41af8cc65358fec41 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 20:30:29 -0800 Subject: [PATCH 09/95] #21-override the setHint method in the password controlField --- .../component/controls/ControlPasswordField.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/component/controls/ControlPasswordField.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/component/controls/ControlPasswordField.java index 7689911a37f..42ca2834da9 100644 --- a/sormas-app/app/src/main/java/de/symeda/sormas/app/component/controls/ControlPasswordField.java +++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/component/controls/ControlPasswordField.java @@ -63,6 +63,19 @@ protected void inflateView(Context context, AttributeSet attrs, int defStyle) { throw new RuntimeException("Unable to inflate layout in " + getClass().getName()); } } + @Override + public void setHint(String hint) { + this.hint = hint; + input.setHint(hint); + } + + @Override + protected String getFieldValue() { + if (input.getText() == null) { + return null; + } + return input.getText().toString(); + } @Override protected void onFinishInflate() { From 1810d6cfd53a407d5d5f61cd91f2a55671a4a4d2 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 20:35:51 -0800 Subject: [PATCH 10/95] #21- created a ChangePassword activity --- .../app/login/ChangePasswordActivity.java | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 sormas-app/app/src/main/java/de/symeda/sormas/app/login/ChangePasswordActivity.java diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/login/ChangePasswordActivity.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/login/ChangePasswordActivity.java new file mode 100644 index 00000000000..eb96ad486b8 --- /dev/null +++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/login/ChangePasswordActivity.java @@ -0,0 +1,233 @@ +package de.symeda.sormas.app.login; + +import android.annotation.SuppressLint; +import android.app.ProgressDialog; +import android.content.Intent; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.core.app.ActivityCompat; +import androidx.databinding.DataBindingUtil; +import de.symeda.sormas.api.utils.DataHelper; +import de.symeda.sormas.app.BaseLocalizedActivity; +import de.symeda.sormas.app.R; +import de.symeda.sormas.app.backend.config.ConfigProvider; +import de.symeda.sormas.app.backend.user.UserDtoHelper; +import de.symeda.sormas.app.core.NotificationContext; +import de.symeda.sormas.app.core.notification.NotificationHelper; +import de.symeda.sormas.app.core.notification.NotificationType; +import de.symeda.sormas.app.databinding.ActivityChangePasswordLayoutBinding; +import de.symeda.sormas.app.rest.RetroProvider; +import de.symeda.sormas.app.settings.SettingsActivity; +import de.symeda.sormas.app.util.NavigationHelper; +import de.symeda.sormas.app.util.SoftKeyboardHelper; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + + +@SuppressLint("ResourceType") +public class ChangePasswordActivity extends BaseLocalizedActivity implements ActivityCompat.OnRequestPermissionsResultCallback, NotificationContext { + + + private boolean isAtLeast8 = false, hasUppercase = false, hasNumber = false, hasSymbol = false, isGood = false; + + private ActivityChangePasswordLayoutBinding binding; + private ProgressDialog progressDialog = null; + + @RequiresApi(api = Build.VERSION_CODES.R) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_change_password_layout); + LoginViewModel loginViewModel = new LoginViewModel(); + binding = DataBindingUtil.setContentView(this, R.layout.activity_change_password_layout); + binding.setData(loginViewModel); + } + + @Override + public void onPause() { + super.onPause(); + + SoftKeyboardHelper.hideKeyboard(this, binding.changePasswordCurrentPassword.getWindowToken()); + } + + @Override + protected void onDestroy() { + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + + super.onDestroy(); + } + + @Override + protected void onResume() { + super.onResume(); + + } + + public void backToSettings(View view) { + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + } + + + + + + + @SuppressLint("ResourceType") + public void registrationDataCheck(View view) { + String newPassword = binding.changePasswordNewPassword.getValue(); + + isAtLeast8 = newPassword.length() >= 8; + hasUppercase = newPassword.matches("(.*[A-Z].*)"); + hasNumber = newPassword.matches("(.*[0-9].*)"); + hasSymbol = newPassword.matches("^(?=.*[_@#%&?;,.()]).*$"); + if (isAtLeast8 && hasNumber && hasUppercase && hasSymbol) { + isGood = true; + binding.actionPasswordStrength.setVisibility(view.getVisibility()); + binding.actionPasswordStrength.setText(R.string.message_password_strong); + binding.actionPasswordStrength.setTextColor(Color.parseColor(getString(R.color.successBackground))); + } else if (isAtLeast8 && hasUppercase && hasNumber) { + isGood = true; + binding.actionPasswordStrength.setVisibility(view.getVisibility()); + binding.actionPasswordStrength.setText(R.string.message_password_moderate); + binding.actionPasswordStrength.setTextColor(Color.parseColor(getString(R.color.brightYellow))); + } else { + binding.actionPasswordStrength.setVisibility(view.getVisibility()); + binding.actionPasswordStrength.setText(R.string.message_password_weak); + binding.actionPasswordStrength.setTextColor(Color.parseColor(getString(R.color.errorBackground))); + } + } + + @SuppressLint("ResourceType") + public void generatePassword(View view) { + if (DataHelper.isNullOrEmpty(ConfigProvider.getServerRestUrl())) { + NavigationHelper.goToSettings(this); + return; + } + try { + RetroProvider.connectAsyncHandled(this, true, true, result -> { + if (Boolean.TRUE.equals(result)) { + try { + executeGeneratePasswordCall(UserDtoHelper.generatePassword()); + } catch (Exception e) { + binding.actionPasswordStrength.setVisibility(view.getVisibility()); + binding.actionPasswordStrength.setText(e.toString()); + binding.actionPasswordStrength.setTextColor(Color.parseColor(getString(R.color.brightYellow))); + } + RetroProvider.disconnect(); + + } + }); + } catch (Exception e) { + binding.actionPasswordStrength.setVisibility(view.getVisibility()); + binding.actionPasswordStrength.setText(e.toString()); + binding.actionPasswordStrength.setTextColor(Color.parseColor(getString(R.color.brightYellow))); + } + } + + @Override + public View getRootView() { + if (binding != null) + return binding.getRoot(); + + return null; + } + + + + + + private void executeGeneratePasswordCall(Call call) { + try { + call.enqueue(new Callback<>() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + if (response.code() != 200) { + NotificationHelper.showNotification(binding, NotificationType.ERROR, R.string.message_could_not_generate_password); + } + var generatedPassword = response.body(); + binding.changePasswordNewPassword.setValue(generatedPassword); + binding.changePasswordConfirmPassword.setValue(generatedPassword); + Toast.makeText(getApplicationContext(),"Password Generated",Toast.LENGTH_SHORT).show(); + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + NotificationHelper.showNotification(binding, NotificationType.ERROR, R.string.message_could_not_generate_password); + } + }); + }catch (Exception e){ + NotificationHelper.showNotification(binding, NotificationType.ERROR, R.string.message_could_not_generate_password); + } + } + + private void executeSaveNewPasswordCall(Call call){ + try { + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.code() != 200) { + NotificationHelper.showNotification(binding, NotificationType.ERROR, R.string.message_could_not_save_password); + }else { + Toast.makeText(getApplicationContext(), "Password Saved", Toast.LENGTH_SHORT).show(); + NotificationHelper.showNotification(binding, NotificationType.SUCCESS, R.string.message_password_changed); + finish(); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + Toast.makeText(getApplicationContext(), "Could not connect to server", Toast.LENGTH_SHORT).show(); + } + }); + }catch (Exception e){ + Log.d("Call",call.toString()); + } + + } + + /** + * When clicked on submit + **/ + public void savePassword(View view) { + if (DataHelper.isNullOrEmpty(ConfigProvider.getServerRestUrl())) { + NavigationHelper.goToSettings(this); + return; + } + String currentPassword = binding.changePasswordCurrentPassword.getValue(); + String newPassword = binding.changePasswordNewPassword.getValue(); + String confirmPassword = binding.changePasswordConfirmPassword.getValue(); + registrationDataCheck(view); + if (!ConfigProvider.getPassword().equals(currentPassword)) { + binding.incorrectCurrentPassword.setVisibility(view.getVisibility()); + } else if (!confirmPassword.equals(newPassword)) { + binding.incorrectConfirmPassword.setVisibility(view.getVisibility()); + } else if (ConfigProvider.getPassword().equals(currentPassword) && confirmPassword.equals(newPassword) && isGood) { + RetroProvider.connectAsyncHandled(this, true, true, result -> { + if (Boolean.TRUE.equals(result)) { + try { + + executeSaveNewPasswordCall(UserDtoHelper.saveNewPassword(ConfigProvider.getUser().getUuid(), newPassword,currentPassword)); + } catch (Exception e) { + binding.actionPasswordStrength.setVisibility(view.getVisibility()); + binding.actionPasswordStrength.setText(e.toString()); + binding.actionPasswordStrength.setTextColor(Color.parseColor(getString(R.color.brightYellow))); + } + RetroProvider.disconnect(); + + } + }); + } + + } +} From cb989442dc3269b8d4efb5d8ff558be3a6054df8 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 20:37:17 -0800 Subject: [PATCH 11/95] #21-Added a post request to save password and a get request to generate password --- .../java/de/symeda/sormas/app/rest/UserFacadeRetro.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/UserFacadeRetro.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/UserFacadeRetro.java index 790099a2015..143aa233c43 100644 --- a/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/UserFacadeRetro.java +++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/rest/UserFacadeRetro.java @@ -23,6 +23,7 @@ import retrofit2.http.GET; import retrofit2.http.POST; import retrofit2.http.Path; +import retrofit2.http.Query; /** * Created by Martin Wahnschaffe on 07.06.2016. @@ -37,4 +38,11 @@ public interface UserFacadeRetro { @GET("users/uuids") Call> pullUuids(); + + + @POST("users/passwordStrength") + Call saveNewPassword(@Query("uuid") String uuid,@Query("newPassword") String newPassword, @Query("currentPassword") String currentPassword); + + @GET("users/generatePassword") + Call generatePassword(); } From e884e2af2f9834dd1423020115f45efb3532cda8 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 22:12:08 -0800 Subject: [PATCH 12/95] #21-Added getters and setters for password strength --- sormas-api/src/main/java/de/symeda/sormas/api/user/UserDto.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserDto.java index 718e928ea53..a74c9eebfa1 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/user/UserDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/user/UserDto.java @@ -71,6 +71,7 @@ public class UserDto extends EntityDto { public static final String PASSWORD = "currentPassword"; public static final String NEW_PASSWORD = "updatePassword"; public static final String CONFIRM_PASSWORD = "confirmPassword"; + public static final String PASSWORD_STRENGTH = "passwordStrength"; private boolean active = true; From 1c5b453e96f98896708919f98d0f9e4734151952 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 22:14:04 -0800 Subject: [PATCH 13/95] #21-Added change password button that leads to changePasswordActivity --- .../de/symeda/sormas/app/settings/SettingsFragment.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sormas-app/app/src/main/java/de/symeda/sormas/app/settings/SettingsFragment.java b/sormas-app/app/src/main/java/de/symeda/sormas/app/settings/SettingsFragment.java index ac66cad5939..c2e6a8a3286 100644 --- a/sormas-app/app/src/main/java/de/symeda/sormas/app/settings/SettingsFragment.java +++ b/sormas-app/app/src/main/java/de/symeda/sormas/app/settings/SettingsFragment.java @@ -57,6 +57,7 @@ import de.symeda.sormas.app.core.notification.NotificationHelper; import de.symeda.sormas.app.databinding.FragmentSettingsLayoutBinding; import de.symeda.sormas.app.lbds.LbdsIntentSender; +import de.symeda.sormas.app.login.ChangePasswordActivity; import de.symeda.sormas.app.login.EnterPinActivity; import de.symeda.sormas.app.login.LoginActivity; import de.symeda.sormas.app.rest.SynchronizeDataAsync; @@ -89,6 +90,7 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, binding.settingsServerUrl.setValue(ConfigProvider.getServerRestUrl()); binding.changePin.setOnClickListener(v -> changePIN()); + binding.changePassword.setOnClickListener(v -> changePassword()); binding.resynchronizeData.setOnClickListener(v -> repullData()); binding.showSyncLog.setOnClickListener(v -> openSyncLog()); binding.logout.setOnClickListener(v -> logout()); @@ -157,6 +159,7 @@ public void onResume() { binding.settingsServerUrlInfo.setVisibility(!hasServerUrl() ? View.VISIBLE : View.GONE); binding.settingsServerUrl.setVisibility(!hasServerUrl() || isShowDevOptions() ? View.VISIBLE : View.GONE); binding.changePin.setVisibility(hasUser ? View.VISIBLE : View.GONE); + binding.changePassword.setVisibility(hasUser ? View.VISIBLE : View.GONE); binding.resynchronizeData.setVisibility(hasUser ? View.VISIBLE : View.GONE); binding.showSyncLog.setVisibility(hasUser ? View.VISIBLE : View.GONE); binding.logout.setVisibility(hasUser && isShowDevOptions() ? View.VISIBLE : View.GONE); @@ -183,6 +186,12 @@ public void changePIN() { intent.putExtra(EnterPinActivity.CALLED_FROM_SETTINGS, true); startActivity(intent); } + public void changePassword() { + Intent intent = new Intent(getActivity(), ChangePasswordActivity.class); + startActivity(intent); + } + + private void repullData() { checkAndShowUnsynchronizedChangesDialog(() -> showRepullDataConfirmationDialog(), "SYNC"); From 1839224786f24b9029d50a2f1394ce1f4468c52e Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 22:15:00 -0800 Subject: [PATCH 14/95] #21-Added curved edges to button --- .../app/src/main/res/drawable/button_round_edges.xml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 sormas-app/app/src/main/res/drawable/button_round_edges.xml diff --git a/sormas-app/app/src/main/res/drawable/button_round_edges.xml b/sormas-app/app/src/main/res/drawable/button_round_edges.xml new file mode 100644 index 00000000000..2f91b0416a5 --- /dev/null +++ b/sormas-app/app/src/main/res/drawable/button_round_edges.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file From ab14391be03f131327831c53266ec76c3090a889 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 22:16:20 -0800 Subject: [PATCH 15/95] #21-created a check icon --- .../app/src/main/res/drawable/ic_baseline_check_.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 sormas-app/app/src/main/res/drawable/ic_baseline_check_.xml diff --git a/sormas-app/app/src/main/res/drawable/ic_baseline_check_.xml b/sormas-app/app/src/main/res/drawable/ic_baseline_check_.xml new file mode 100644 index 00000000000..0432fa69bb7 --- /dev/null +++ b/sormas-app/app/src/main/res/drawable/ic_baseline_check_.xml @@ -0,0 +1,10 @@ + + + From c8a9021e5d5b4e489395c8c99d3f5e94f813c068 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 22:22:27 -0800 Subject: [PATCH 16/95] #21-Added change password button --- .../src/main/res/layout/fragment_settings_layout.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sormas-app/app/src/main/res/layout/fragment_settings_layout.xml b/sormas-app/app/src/main/res/layout/fragment_settings_layout.xml index af484c7e9aa..9f533b4ec61 100644 --- a/sormas-app/app/src/main/res/layout/fragment_settings_layout.xml +++ b/sormas-app/app/src/main/res/layout/fragment_settings_layout.xml @@ -71,6 +71,16 @@ app:buttonType="@{ControlButtonType.LINE_SECONDARY}" app:rounded="true" /> + + Date: Mon, 21 Feb 2022 22:26:53 -0800 Subject: [PATCH 17/95] #21-Edited the textInputfield to change hint --- sormas-app/app/src/main/res/layout/control_password_layout.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/sormas-app/app/src/main/res/layout/control_password_layout.xml b/sormas-app/app/src/main/res/layout/control_password_layout.xml index 7aacb40d551..f7f5cf6ceae 100644 --- a/sormas-app/app/src/main/res/layout/control_password_layout.xml +++ b/sormas-app/app/src/main/res/layout/control_password_layout.xml @@ -39,6 +39,7 @@ android:id="@+id/text_input" android:layout_width="match_parent" android:inputType="textPassword" + android:hint="" style="@style/ControlTextFieldStyle" /> From 5036d4f9a3ce603793f570e5bb08a0c4011a8063 Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 22:34:15 -0800 Subject: [PATCH 18/95] #21-Created a validator for the password field --- .../ui/utils/UpdatePasswordValidator.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 sormas-ui/src/main/java/de/symeda/sormas/ui/utils/UpdatePasswordValidator.java diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/UpdatePasswordValidator.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/UpdatePasswordValidator.java new file mode 100644 index 00000000000..36175288ffb --- /dev/null +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/UpdatePasswordValidator.java @@ -0,0 +1,28 @@ +package de.symeda.sormas.ui.utils; + +import com.vaadin.v7.data.validator.AbstractValidator; + +public class UpdatePasswordValidator extends AbstractValidator { + + /** + * Constructs a validator with the given error message. + * + * @param errorMessage + * the message to be included in an {@link InvalidValueException} + * (with "{0}" replaced by the value that failed validation). + */ + public UpdatePasswordValidator(String errorMessage) { + super(errorMessage); + } + + @Override + protected boolean isValidValue(String password) { + String pattern = "(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}"; + return password == null || password.isEmpty() || password.matches(pattern); + } + + @Override + public Class getType() { + return String.class; + } +} From 0ee7ee28f078fb8225f73845455e5d3e4a937edb Mon Sep 17 00:00:00 2001 From: Anthony4m Date: Mon, 21 Feb 2022 22:36:10 -0800 Subject: [PATCH 19/95] #21-Added a change password layout --- .../activity_change_password_layout.xml | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 sormas-app/app/src/main/res/layout/activity_change_password_layout.xml diff --git a/sormas-app/app/src/main/res/layout/activity_change_password_layout.xml b/sormas-app/app/src/main/res/layout/activity_change_password_layout.xml new file mode 100644 index 00000000000..884ee4c832b --- /dev/null +++ b/sormas-app/app/src/main/res/layout/activity_change_password_layout.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +