From 19e7d0828bd7980bbe0798acf91e06ce7d302cf5 Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Sat, 9 Mar 2024 20:48:53 +0200 Subject: [PATCH 01/12] If OldPassword is passed, we check that the new password does not match the OldPassword Also in this case we check that the old password matches the entered old password #328 --- .../App/Password/src/PasswordDialog.Page.al | 24 ++++++++++ .../src/PasswordDialogImpl.Codeunit.al | 1 + .../src/PasswordDialogManagement.Codeunit.al | 2 +- .../src/PasswordDialogTest.Codeunit.al | 45 ++++++++++++++++++- 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/System Application/App/Password/src/PasswordDialog.Page.al b/src/System Application/App/Password/src/PasswordDialog.Page.al index 6a6a659802..76bf2db6f4 100644 --- a/src/System Application/App/Password/src/PasswordDialog.Page.al +++ b/src/System Application/App/Password/src/PasswordDialog.Page.al @@ -27,6 +27,12 @@ page 9810 "Password Dialog" ExtendedDatatype = Masked; ToolTip = 'Specifies the current password, before the user defines a new one.'; Visible = ShowOldPassword; + trigger OnValidate() + begin + if OldPasswordToCompare <> '' then + if OldPasswordToCompare <> OldPasswordValue then + Error(OldPasswordMismatchErr); + end; } field(Password; PasswordValue) { @@ -39,6 +45,10 @@ page 9810 "Password Dialog" begin if RequiresPasswordValidation then PasswordDialogImpl.ValidatePasswordStrength(PasswordValue); + + if OldPasswordToCompare <> '' then + if OldPasswordToCompare = PasswordValue then + Error(PasswordSameAsOldErr); end; } field(ConfirmPassword; ConfirmPasswordValue) @@ -85,12 +95,16 @@ page 9810 "Password Dialog" var PasswordDialogImpl: Codeunit "Password Dialog Impl."; PasswordMismatchErr: Label 'The passwords that you entered do not match.'; + PasswordSameAsOldErr: Label 'The new password cannot be the same as the old password.'; + OldPasswordMismatchErr: Label 'The old password does not match the entered password.'; [NonDebuggable] PasswordValue: Text; [NonDebuggable] ConfirmPasswordValue: Text; [NonDebuggable] OldPasswordValue: Text; + [NonDebuggable] + OldPasswordToCompare: Text; ShowOldPassword: Boolean; ValidPassword: Boolean; RequiresPasswordValidation: Boolean; @@ -150,6 +164,16 @@ page 9810 "Password Dialog" Password := OldPasswordValue; end; + /// + /// Set the old password value to compare with typed on the page. + /// + /// Old password to compare. + [Scope('OnPrem')] + procedure SetOldPasswordToCompareSecretValue(OldPassword: SecretText) + begin + OldPasswordToCompare := OldPassword.Unwrap(); + end; + /// /// Enables the Change password mode, it makes the old password field on the page visible. /// diff --git a/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al b/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al index 3b700c7e76..40af87601a 100644 --- a/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al +++ b/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al @@ -73,6 +73,7 @@ codeunit 9811 "Password Dialog Impl." PasswordDialog: Page "Password Dialog"; begin PasswordDialog.EnableChangePassword(); + PasswordDialog.SetOldPasswordToCompareSecretValue(OldPassword); if PasswordDialog.RunModal() = Action::OK then begin Password := PasswordDialog.GetPasswordSecretValue(); OldPassword := PasswordDialog.GetOldPasswordSecretValue(); diff --git a/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al b/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al index a660d45df9..36d1078bdd 100644 --- a/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al +++ b/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al @@ -116,7 +116,7 @@ codeunit 9810 "Password Dialog Management" /// Opens a dialog for the user to change a password and returns the old and new typed passwords if there is no validation error, /// otherwise an empty text are returned. /// - /// Out parameter, the old password user typed on the dialog. + /// Out parameter, the old password to compare with the password user typed on the dialog. /// Out parameter, the new password user typed on the dialog. procedure OpenChangePasswordDialog(var OldPassword: SecretText; var Password: SecretText) begin diff --git a/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al b/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al index 1753ef9bee..49929041fd 100644 --- a/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al +++ b/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al @@ -26,8 +26,11 @@ codeunit 135033 "Password Dialog Test" DisablePasswordValidation: Boolean; PasswordMissmatch: Boolean; ValidPasswordTxt: Label 'Some Password 2!'; + AnotherValidPasswordTxt: Label 'Some Password 3!'; InValidPasswordTxt: Label 'Some Password'; AnotherPasswordTxt: Label 'Another Password'; + PasswordSameAsOldErr: Label 'The new password cannot be the same as the old password.'; + OldPasswordMismatchErr: Label 'The old password does not match the entered password.'; [Test] [HandlerFunctions('PasswordDialogModalPageHandler')] @@ -221,6 +224,29 @@ codeunit 135033 "Password Dialog Test" Assert.AreEqual(ValidPasswordTxt, GetPasswordValue(Password), 'A diferrent password was expected.') end; + [Test] + [HandlerFunctions('ChangePasswordDialogWitldOldPasswordModalPageHandler')] + procedure OpenChangePasswordDialogWithOldPasswordTest(); + var + Password: SecretText; + OldPassword: SecretText; + begin + // [SCENARIO] Open Password dialog in change password mode. Old password + // The old password has been passed on. + PermissionsMock.Set('All Objects'); + + // [GIVEN] The old password is for comparison that the old password matches the entered user and + // the new password does not match the old password. + OldPassword := SecretStrSubstNo(ValidPasswordTxt); + + // [WHEN] The password dialog is opened in change password mode. + PasswordDialogManagement.OpenChangePasswordDialog(OldPassword, Password); + + // [THEN] The Old and New passwords are retrieved. + Assert.AreEqual(ValidPasswordTxt, GetPasswordValue(OldPassword), 'A diferrent password was expected.'); + Assert.AreEqual(AnotherValidPasswordTxt, GetPasswordValue(Password), 'A diferrent password was expected.') + end; + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Password Dialog Management", 'OnSetMinPasswordLength', '', true, true)] local procedure OnSetMinimumPAsswordLength(var MinPasswordLength: Integer); begin @@ -243,14 +269,29 @@ codeunit 135033 "Password Dialog Test" [ModalPageHandler] procedure ChangePasswordDialogModalPageHandler(var PasswordDialog: TestPage "Password Dialog"); begin - Assert.IsTrue(PasswordDialog.OldPassword.Visible(), 'Old Password Field should not be visible.'); - Assert.IsTrue(PasswordDialog.ConfirmPassword.Visible(), 'Confirm Password Field should not be visible.'); + Assert.IsTrue(PasswordDialog.OldPassword.Visible(), 'Old Password Field should be visible.'); + Assert.IsTrue(PasswordDialog.ConfirmPassword.Visible(), 'Confirm Password Field should be visible.'); PasswordDialog.OldPassword.SetValue(InValidPasswordTxt); PasswordDialog.Password.SetValue(ValidPasswordTxt); PasswordDialog.ConfirmPassword.SetValue(ValidPasswordTxt); PasswordDialog.OK().Invoke(); end; + [ModalPageHandler] + procedure ChangePasswordDialogWitldOldPasswordModalPageHandler(var PasswordDialog: TestPage "Password Dialog"); + begin + Assert.IsTrue(PasswordDialog.OldPassword.Visible(), 'Old Password Field should be visible.'); + Assert.IsTrue(PasswordDialog.ConfirmPassword.Visible(), 'Confirm Password Field should be visible.'); + asserterror PasswordDialog.OldPassword.SetValue(InValidPasswordTxt); + Assert.ExpectedError(OldPasswordMismatchErr); + PasswordDialog.OldPassword.SetValue(ValidPasswordTxt); + asserterror PasswordDialog.Password.SetValue(ValidPasswordTxt); + Assert.ExpectedError(PasswordSameAsOldErr); + PasswordDialog.Password.SetValue(AnotherValidPasswordTxt); + PasswordDialog.ConfirmPassword.SetValue(AnotherValidPasswordTxt); + PasswordDialog.OK().Invoke(); + end; + [ConfirmHandler] procedure ConfirmHandler(Question: Text[1024]; var Reply: Boolean); begin From 0cc528bd1b7eb50e9981b2f494cfa9af0d2fb713 Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Mon, 11 Mar 2024 20:47:50 +0200 Subject: [PATCH 02/12] Transferring password rules to PasswordDialogImpl #328 --- .../App/Password/src/PasswordDialog.Page.al | 15 ++++----------- .../src/PasswordDialogImpl.Codeunit.al | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/System Application/App/Password/src/PasswordDialog.Page.al b/src/System Application/App/Password/src/PasswordDialog.Page.al index 76bf2db6f4..7ef0ce6e06 100644 --- a/src/System Application/App/Password/src/PasswordDialog.Page.al +++ b/src/System Application/App/Password/src/PasswordDialog.Page.al @@ -29,9 +29,7 @@ page 9810 "Password Dialog" Visible = ShowOldPassword; trigger OnValidate() begin - if OldPasswordToCompare <> '' then - if OldPasswordToCompare <> OldPasswordValue then - Error(OldPasswordMismatchErr); + PasswordDialogImpl.ValidateOldPasswordMatch(OldPasswordToCompare, OldPasswordValue); end; } field(Password; PasswordValue) @@ -46,9 +44,7 @@ page 9810 "Password Dialog" if RequiresPasswordValidation then PasswordDialogImpl.ValidatePasswordStrength(PasswordValue); - if OldPasswordToCompare <> '' then - if OldPasswordToCompare = PasswordValue then - Error(PasswordSameAsOldErr); + PasswordDialogImpl.ValidateNewPasswordUniqueness(OldPasswordToCompare, PasswordValue); end; } field(ConfirmPassword; ConfirmPasswordValue) @@ -95,16 +91,13 @@ page 9810 "Password Dialog" var PasswordDialogImpl: Codeunit "Password Dialog Impl."; PasswordMismatchErr: Label 'The passwords that you entered do not match.'; - PasswordSameAsOldErr: Label 'The new password cannot be the same as the old password.'; - OldPasswordMismatchErr: Label 'The old password does not match the entered password.'; [NonDebuggable] PasswordValue: Text; [NonDebuggable] ConfirmPasswordValue: Text; [NonDebuggable] OldPasswordValue: Text; - [NonDebuggable] - OldPasswordToCompare: Text; + OldPasswordToCompare: SecretText; ShowOldPassword: Boolean; ValidPassword: Boolean; RequiresPasswordValidation: Boolean; @@ -171,7 +164,7 @@ page 9810 "Password Dialog" [Scope('OnPrem')] procedure SetOldPasswordToCompareSecretValue(OldPassword: SecretText) begin - OldPasswordToCompare := OldPassword.Unwrap(); + OldPasswordToCompare := OldPassword; end; /// diff --git a/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al b/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al index 40af87601a..615b09a7f2 100644 --- a/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al +++ b/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al @@ -15,6 +15,8 @@ codeunit 9811 "Password Dialog Impl." PasswordMismatchErr: Label 'The passwords that you entered do not match.'; PasswordTooSimpleErr: Label 'The password that you entered does not meet the minimum requirements. It must be at least %1 characters long and contain at least one uppercase letter, one lowercase letter, one number and one special character. It must not have a sequence of 3 or more ascending, descending or repeating characters.', Comment = '%1: The minimum number of characters required in the password'; ConfirmBlankPasswordQst: Label 'Do you want to exit without entering a password?'; + PasswordSameAsOldErr: Label 'The new password cannot be the same as the old password.'; + OldPasswordMismatchErr: Label 'The old password does not match the entered password.'; procedure ValidatePasswordStrength(Password: SecretText) var @@ -93,5 +95,21 @@ codeunit 9811 "Password Dialog Impl." exit(false); exit(true); end; + + [NonDebuggable] + procedure ValidateOldPasswordMatch(OldPasswordToCompare: SecretText; OldPasswordEntered: SecretText) + begin + if OldPasswordToCompare.Unwrap() <> '' then + if OldPasswordToCompare.Unwrap() <> OldPasswordEntered.Unwrap() then + Error(OldPasswordMismatchErr); + end; + + [NonDebuggable] + procedure ValidateNewPasswordUniqueness(OldPasswordToCompare: SecretText; NewPassword: SecretText) + begin + if OldPasswordToCompare.Unwrap() <> '' then + if OldPasswordToCompare.Unwrap() = NewPassword.Unwrap() then + Error(PasswordSameAsOldErr); + end; } From b9d4f042b73046e6ef1528f817b32940802724c0 Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Tue, 12 Mar 2024 12:04:21 +0200 Subject: [PATCH 03/12] Use IsEmpty instead of unwrap to check if secret is empty --- .../App/Password/src/PasswordDialogImpl.Codeunit.al | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al b/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al index 615b09a7f2..b8409f3c56 100644 --- a/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al +++ b/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al @@ -99,7 +99,7 @@ codeunit 9811 "Password Dialog Impl." [NonDebuggable] procedure ValidateOldPasswordMatch(OldPasswordToCompare: SecretText; OldPasswordEntered: SecretText) begin - if OldPasswordToCompare.Unwrap() <> '' then + if not OldPasswordToCompare.IsEmpty() then if OldPasswordToCompare.Unwrap() <> OldPasswordEntered.Unwrap() then Error(OldPasswordMismatchErr); end; @@ -107,7 +107,7 @@ codeunit 9811 "Password Dialog Impl." [NonDebuggable] procedure ValidateNewPasswordUniqueness(OldPasswordToCompare: SecretText; NewPassword: SecretText) begin - if OldPasswordToCompare.Unwrap() <> '' then + if not OldPasswordToCompare.IsEmpty() then if OldPasswordToCompare.Unwrap() = NewPassword.Unwrap() then Error(PasswordSameAsOldErr); end; From 76046dc42f47c7dc38a34417a3efba3c34f76f24 Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Tue, 12 Mar 2024 19:40:56 +0200 Subject: [PATCH 04/12] Update input param name to be unique in scope #328 --- .../App/Password/src/PasswordDialog.Page.al | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/System Application/App/Password/src/PasswordDialog.Page.al b/src/System Application/App/Password/src/PasswordDialog.Page.al index 7ef0ce6e06..1ae91a994c 100644 --- a/src/System Application/App/Password/src/PasswordDialog.Page.al +++ b/src/System Application/App/Password/src/PasswordDialog.Page.al @@ -160,11 +160,11 @@ page 9810 "Password Dialog" /// /// Set the old password value to compare with typed on the page. /// - /// Old password to compare. + /// Old password to compare. [Scope('OnPrem')] - procedure SetOldPasswordToCompareSecretValue(OldPassword: SecretText) + procedure SetOldPasswordToCompareSecretValue(OldPasswordSecret: SecretText) begin - OldPasswordToCompare := OldPassword; + OldPasswordToCompare := OldPasswordSecret; end; /// From 6764f574920e341d9d106a176e86738ff2d7b0b8 Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Wed, 13 Mar 2024 15:15:56 +0200 Subject: [PATCH 05/12] Reduce nested condition with exit --- .../Password/src/PasswordDialogImpl.Codeunit.al | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al b/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al index b8409f3c56..14fe141620 100644 --- a/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al +++ b/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al @@ -99,17 +99,19 @@ codeunit 9811 "Password Dialog Impl." [NonDebuggable] procedure ValidateOldPasswordMatch(OldPasswordToCompare: SecretText; OldPasswordEntered: SecretText) begin - if not OldPasswordToCompare.IsEmpty() then - if OldPasswordToCompare.Unwrap() <> OldPasswordEntered.Unwrap() then - Error(OldPasswordMismatchErr); + if OldPasswordToCompare.IsEmpty() then + exit; + if OldPasswordToCompare.Unwrap() <> OldPasswordEntered.Unwrap() then + Error(OldPasswordMismatchErr); end; [NonDebuggable] procedure ValidateNewPasswordUniqueness(OldPasswordToCompare: SecretText; NewPassword: SecretText) begin - if not OldPasswordToCompare.IsEmpty() then - if OldPasswordToCompare.Unwrap() = NewPassword.Unwrap() then - Error(PasswordSameAsOldErr); + if OldPasswordToCompare.IsEmpty() then + exit; + if OldPasswordToCompare.Unwrap() = NewPassword.Unwrap() then + Error(PasswordSameAsOldErr); end; } From 33edd782529161c357a638ec1e7f6c380ac7125a Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Mon, 25 Mar 2024 16:04:18 +0200 Subject: [PATCH 06/12] Fix typos for #328 --- .../Test/Password/src/PasswordDialogTest.Codeunit.al | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al b/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al index 49929041fd..032859ea1c 100644 --- a/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al +++ b/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al @@ -225,13 +225,13 @@ codeunit 135033 "Password Dialog Test" end; [Test] - [HandlerFunctions('ChangePasswordDialogWitldOldPasswordModalPageHandler')] + [HandlerFunctions('ChangePasswordDialogWithOldPasswordModalPageHandler')] procedure OpenChangePasswordDialogWithOldPasswordTest(); var Password: SecretText; OldPassword: SecretText; begin - // [SCENARIO] Open Password dialog in change password mode. Old password + // [SCENARIO] Open Password dialog in change password mode. // The old password has been passed on. PermissionsMock.Set('All Objects'); @@ -278,7 +278,7 @@ codeunit 135033 "Password Dialog Test" end; [ModalPageHandler] - procedure ChangePasswordDialogWitldOldPasswordModalPageHandler(var PasswordDialog: TestPage "Password Dialog"); + procedure ChangePasswordDialogWithOldPasswordModalPageHandler(var PasswordDialog: TestPage "Password Dialog"); begin Assert.IsTrue(PasswordDialog.OldPassword.Visible(), 'Old Password Field should be visible.'); Assert.IsTrue(PasswordDialog.ConfirmPassword.Visible(), 'Confirm Password Field should be visible.'); From c6c2f7eb40933a107a22e099ab13ce163e957066 Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Tue, 21 May 2024 07:47:28 +0300 Subject: [PATCH 07/12] Added a new method to replace the obsolete method for password entry Used the old implementation for the OnPrem report Updated tests Renamed old password variable to current password --- .../App/Password/src/ChangePassword.Report.al | 4 +- .../App/Password/src/PasswordDialog.Page.al | 10 ++--- .../src/PasswordDialogImpl.Codeunit.al | 31 ++++++++----- .../src/PasswordDialogManagement.Codeunit.al | 22 ++++++--- .../src/PasswordDialogTest.Codeunit.al | 45 ++++++++++++++----- 5 files changed, 77 insertions(+), 35 deletions(-) diff --git a/src/System Application/App/Password/src/ChangePassword.Report.al b/src/System Application/App/Password/src/ChangePassword.Report.al index 2ca90d7cfa..07bf125c4d 100644 --- a/src/System Application/App/Password/src/ChangePassword.Report.al +++ b/src/System Application/App/Password/src/ChangePassword.Report.al @@ -27,11 +27,11 @@ report 9810 "Change Password" trigger OnInitReport() var User: Record User; - PasswordDialogManagement: Codeunit "Password Dialog Management"; + PasswordDialogImpl: Codeunit "Password Dialog Impl."; Password: SecretText; OldPassword: SecretText; begin - PasswordDialogManagement.OpenChangePasswordDialog(OldPassword, Password); + PasswordDialogImpl.OpenChangePasswordDialog(OldPassword, Password); if Password.IsEmpty() then exit; diff --git a/src/System Application/App/Password/src/PasswordDialog.Page.al b/src/System Application/App/Password/src/PasswordDialog.Page.al index 1ae91a994c..02b91c2664 100644 --- a/src/System Application/App/Password/src/PasswordDialog.Page.al +++ b/src/System Application/App/Password/src/PasswordDialog.Page.al @@ -29,7 +29,7 @@ page 9810 "Password Dialog" Visible = ShowOldPassword; trigger OnValidate() begin - PasswordDialogImpl.ValidateOldPasswordMatch(OldPasswordToCompare, OldPasswordValue); + PasswordDialogImpl.ValidateOldPasswordMatch(CurrentPasswordToCompare, OldPasswordValue); end; } field(Password; PasswordValue) @@ -44,7 +44,7 @@ page 9810 "Password Dialog" if RequiresPasswordValidation then PasswordDialogImpl.ValidatePasswordStrength(PasswordValue); - PasswordDialogImpl.ValidateNewPasswordUniqueness(OldPasswordToCompare, PasswordValue); + PasswordDialogImpl.ValidateNewPasswordUniqueness(CurrentPasswordToCompare, PasswordValue); end; } field(ConfirmPassword; ConfirmPasswordValue) @@ -97,7 +97,7 @@ page 9810 "Password Dialog" ConfirmPasswordValue: Text; [NonDebuggable] OldPasswordValue: Text; - OldPasswordToCompare: SecretText; + CurrentPasswordToCompare: SecretText; ShowOldPassword: Boolean; ValidPassword: Boolean; RequiresPasswordValidation: Boolean; @@ -162,9 +162,9 @@ page 9810 "Password Dialog" /// /// Old password to compare. [Scope('OnPrem')] - procedure SetOldPasswordToCompareSecretValue(OldPasswordSecret: SecretText) + procedure SetCurrentPasswordToCompareSecretValue(CurrentPasswordSecret: SecretText) begin - OldPasswordToCompare := OldPasswordSecret; + CurrentPasswordToCompare := CurrentPasswordSecret; end; /// diff --git a/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al b/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al index 14fe141620..afecdde6c4 100644 --- a/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al +++ b/src/System Application/App/Password/src/PasswordDialogImpl.Codeunit.al @@ -15,8 +15,8 @@ codeunit 9811 "Password Dialog Impl." PasswordMismatchErr: Label 'The passwords that you entered do not match.'; PasswordTooSimpleErr: Label 'The password that you entered does not meet the minimum requirements. It must be at least %1 characters long and contain at least one uppercase letter, one lowercase letter, one number and one special character. It must not have a sequence of 3 or more ascending, descending or repeating characters.', Comment = '%1: The minimum number of characters required in the password'; ConfirmBlankPasswordQst: Label 'Do you want to exit without entering a password?'; - PasswordSameAsOldErr: Label 'The new password cannot be the same as the old password.'; - OldPasswordMismatchErr: Label 'The old password does not match the entered password.'; + PasswordSameAsNewErr: Label 'The new password cannot be the same as the current password.'; + CurrentPasswordMismatchErr: Label 'The current password does not match the entered password.'; procedure ValidatePasswordStrength(Password: SecretText) var @@ -75,13 +75,22 @@ codeunit 9811 "Password Dialog Impl." PasswordDialog: Page "Password Dialog"; begin PasswordDialog.EnableChangePassword(); - PasswordDialog.SetOldPasswordToCompareSecretValue(OldPassword); if PasswordDialog.RunModal() = Action::OK then begin Password := PasswordDialog.GetPasswordSecretValue(); OldPassword := PasswordDialog.GetOldPasswordSecretValue(); end; end; + procedure OpenPasswordChangeDialog(CurrentPassword: SecretText; var NewPassword: SecretText) + var + PasswordDialog: Page "Password Dialog"; + begin + PasswordDialog.EnableChangePassword(); + PasswordDialog.SetCurrentPasswordToCompareSecretValue(CurrentPassword); + if PasswordDialog.RunModal() = Action::OK then + NewPassword := PasswordDialog.GetPasswordSecretValue(); + end; + [NonDebuggable] procedure ValidatePassword(RequiresPasswordConfirmation: Boolean; RequiresPasswordValidation: Boolean; Password: SecretText; ConfirmPassword: SecretText): Boolean begin @@ -97,21 +106,21 @@ codeunit 9811 "Password Dialog Impl." end; [NonDebuggable] - procedure ValidateOldPasswordMatch(OldPasswordToCompare: SecretText; OldPasswordEntered: SecretText) + procedure ValidateOldPasswordMatch(CurrentPasswordToCompare: SecretText; OldPasswordEntered: SecretText) begin - if OldPasswordToCompare.IsEmpty() then + if CurrentPasswordToCompare.IsEmpty() then exit; - if OldPasswordToCompare.Unwrap() <> OldPasswordEntered.Unwrap() then - Error(OldPasswordMismatchErr); + if CurrentPasswordToCompare.Unwrap() <> OldPasswordEntered.Unwrap() then + Error(CurrentPasswordMismatchErr); end; [NonDebuggable] - procedure ValidateNewPasswordUniqueness(OldPasswordToCompare: SecretText; NewPassword: SecretText) + procedure ValidateNewPasswordUniqueness(CurrentPasswordToCompare: SecretText; NewPassword: SecretText) begin - if OldPasswordToCompare.IsEmpty() then + if CurrentPasswordToCompare.IsEmpty() then exit; - if OldPasswordToCompare.Unwrap() = NewPassword.Unwrap() then - Error(PasswordSameAsOldErr); + if CurrentPasswordToCompare.Unwrap() = NewPassword.Unwrap() then + Error(PasswordSameAsNewErr); end; } diff --git a/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al b/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al index 36d1078bdd..50e34e0637 100644 --- a/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al +++ b/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al @@ -77,6 +77,18 @@ codeunit 9810 "Password Dialog Management" PasswordDialogImpl.OpenChangePasswordDialog(OldPassword, Password); #pragma warning restore AL0432 end; + + /// + /// Opens a dialog for the user to change a password and returns the old and new typed passwords if there is no validation error, + /// otherwise an empty text are returned. + /// + /// Out parameter, the old password user typed on the dialog. + /// Out parameter, the new password user typed on the dialog. + [Obsolete('Replaced by OpenPasswordChangeDialog without out OldPassword param', '24.0')] + procedure OpenChangePasswordDialog(var OldPassword: SecretText; var Password: SecretText) + begin + PasswordDialogImpl.OpenChangePasswordDialog(OldPassword, Password); + end; #endif /// @@ -113,14 +125,14 @@ codeunit 9810 "Password Dialog Management" end; /// - /// Opens a dialog for the user to change a password and returns the old and new typed passwords if there is no validation error, + /// Opens a dialog for the user to change a password and returns the new typed password if there is no validation error, /// otherwise an empty text are returned. /// - /// Out parameter, the old password to compare with the password user typed on the dialog. - /// Out parameter, the new password user typed on the dialog. - procedure OpenChangePasswordDialog(var OldPassword: SecretText; var Password: SecretText) + /// In parameter, the current password to compare with the password user typed on the dialog. + /// Out parameter, the new password user typed on the dialog. + procedure OpenPasswordChangeDialog(CurrentPassword: SecretText; var NewPassword: SecretText) begin - PasswordDialogImpl.OpenChangePasswordDialog(OldPassword, Password); + PasswordDialogImpl.OpenPasswordChangeDialog(CurrentPassword, NewPassword); end; /// diff --git a/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al b/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al index 032859ea1c..596ca6691b 100644 --- a/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al +++ b/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al @@ -29,8 +29,8 @@ codeunit 135033 "Password Dialog Test" AnotherValidPasswordTxt: Label 'Some Password 3!'; InValidPasswordTxt: Label 'Some Password'; AnotherPasswordTxt: Label 'Another Password'; - PasswordSameAsOldErr: Label 'The new password cannot be the same as the old password.'; - OldPasswordMismatchErr: Label 'The old password does not match the entered password.'; + PasswordSameAsNewErr: Label 'The new password cannot be the same as the current password.'; + CurrentPasswordMismatchErr: Label 'The current password does not match the entered password.'; [Test] [HandlerFunctions('PasswordDialogModalPageHandler')] @@ -210,14 +210,17 @@ codeunit 135033 "Password Dialog Test" [HandlerFunctions('ChangePasswordDialogModalPageHandler')] procedure OpenChangePasswordDialogTest(); var + PasswordDialogImpl: Codeunit "Password Dialog Impl."; Password: SecretText; OldPassword: SecretText; begin // [SCENARIO] Open Password dialog in change password mode. + // Used for "Change Password" OnPrem report. + // Internal implementation to support OnPrem Database.ChangeUserPassword PermissionsMock.Set('All Objects'); // [WHEN] The password dialog is opened in change password mode. - PasswordDialogManagement.OpenChangePasswordDialog(OldPassword, Password); + PasswordDialogImpl.OpenChangePasswordDialog(OldPassword, Password); // [THEN] The Old and New passwords are retrieved. Assert.AreEqual(InValidPasswordTxt, GetPasswordValue(OldPassword), 'A diferrent password was expected.'); @@ -225,11 +228,29 @@ codeunit 135033 "Password Dialog Test" end; [Test] - [HandlerFunctions('ChangePasswordDialogWithOldPasswordModalPageHandler')] - procedure OpenChangePasswordDialogWithOldPasswordTest(); + [HandlerFunctions('ChangePasswordDialogModalPageHandler')] + procedure OpenPasswordChangeDialogTest(); var Password: SecretText; - OldPassword: SecretText; + CurrentPassword: SecretText; + begin + // [SCENARIO] Open Password dialog in change password mode. + PermissionsMock.Set('All Objects'); + + // [WHEN] The password dialog is opened in change password mode. + PasswordDialogManagement.OpenChangePasswordDialog(CurrentPassword, Password); + + // [THEN] The Old and New passwords are retrieved. + Assert.AreEqual(InValidPasswordTxt, GetPasswordValue(CurrentPassword), 'A diferrent password was expected.'); + Assert.AreEqual(ValidPasswordTxt, GetPasswordValue(Password), 'A diferrent password was expected.') + end; + + [Test] + [HandlerFunctions('ChangePasswordDialogWithCurrentPasswordModalPageHandler')] + procedure OpenPasswordChangeDialogWithCurrentPasswordTest(); + var + Password: SecretText; + CurrentPassword: SecretText; begin // [SCENARIO] Open Password dialog in change password mode. // The old password has been passed on. @@ -237,13 +258,13 @@ codeunit 135033 "Password Dialog Test" // [GIVEN] The old password is for comparison that the old password matches the entered user and // the new password does not match the old password. - OldPassword := SecretStrSubstNo(ValidPasswordTxt); + CurrentPassword := SecretStrSubstNo(ValidPasswordTxt); // [WHEN] The password dialog is opened in change password mode. - PasswordDialogManagement.OpenChangePasswordDialog(OldPassword, Password); + PasswordDialogManagement.OpenPasswordChangeDialog(CurrentPassword, Password); // [THEN] The Old and New passwords are retrieved. - Assert.AreEqual(ValidPasswordTxt, GetPasswordValue(OldPassword), 'A diferrent password was expected.'); + Assert.AreEqual(ValidPasswordTxt, GetPasswordValue(CurrentPassword), 'A diferrent password was expected.'); Assert.AreEqual(AnotherValidPasswordTxt, GetPasswordValue(Password), 'A diferrent password was expected.') end; @@ -278,15 +299,15 @@ codeunit 135033 "Password Dialog Test" end; [ModalPageHandler] - procedure ChangePasswordDialogWithOldPasswordModalPageHandler(var PasswordDialog: TestPage "Password Dialog"); + procedure ChangePasswordDialogWithCurrentPasswordModalPageHandler(var PasswordDialog: TestPage "Password Dialog"); begin Assert.IsTrue(PasswordDialog.OldPassword.Visible(), 'Old Password Field should be visible.'); Assert.IsTrue(PasswordDialog.ConfirmPassword.Visible(), 'Confirm Password Field should be visible.'); asserterror PasswordDialog.OldPassword.SetValue(InValidPasswordTxt); - Assert.ExpectedError(OldPasswordMismatchErr); + Assert.ExpectedError(CurrentPasswordMismatchErr); PasswordDialog.OldPassword.SetValue(ValidPasswordTxt); asserterror PasswordDialog.Password.SetValue(ValidPasswordTxt); - Assert.ExpectedError(PasswordSameAsOldErr); + Assert.ExpectedError(PasswordSameAsNewErr); PasswordDialog.Password.SetValue(AnotherValidPasswordTxt); PasswordDialog.ConfirmPassword.SetValue(AnotherValidPasswordTxt); PasswordDialog.OK().Invoke(); From 1502d1f57fc3161e0ec7df813ae7424d45e821f0 Mon Sep 17 00:00:00 2001 From: Jesper Schulz-Wedde Date: Tue, 21 May 2024 12:58:52 +0200 Subject: [PATCH 08/12] Update src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al --- .../App/Password/src/PasswordDialogManagement.Codeunit.al | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al b/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al index 50e34e0637..54dd0e9d26 100644 --- a/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al +++ b/src/System Application/App/Password/src/PasswordDialogManagement.Codeunit.al @@ -84,7 +84,7 @@ codeunit 9810 "Password Dialog Management" /// /// Out parameter, the old password user typed on the dialog. /// Out parameter, the new password user typed on the dialog. - [Obsolete('Replaced by OpenPasswordChangeDialog without out OldPassword param', '24.0')] + [Obsolete('Replaced by OpenPasswordChangeDialog without out OldPassword param', '25.0')] procedure OpenChangePasswordDialog(var OldPassword: SecretText; var Password: SecretText) begin PasswordDialogImpl.OpenChangePasswordDialog(OldPassword, Password); From e84b0a2ebafd135a830316f90c568eab191be07f Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Tue, 21 May 2024 18:12:33 +0300 Subject: [PATCH 09/12] Move OpenChangePasswordDialog to Library --- .../Test Library/Password/app.json | 4 +-- .../Password/src/LibraryPassword.Codeunit.al | 25 +++++++++++++++++++ .../src/PasswordDialogTest.Codeunit.al | 4 +-- 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 src/System Application/Test Library/Password/src/LibraryPassword.Codeunit.al diff --git a/src/System Application/Test Library/Password/app.json b/src/System Application/Test Library/Password/app.json index fe513ed383..76877e7b91 100644 --- a/src/System Application/Test Library/Password/app.json +++ b/src/System Application/Test Library/Password/app.json @@ -23,8 +23,8 @@ ], "idRanges": [ { - "from": 135033, - "to": 135033 + "from": 132528, + "to": 132528 } ], "platform": "25.0.0.0", diff --git a/src/System Application/Test Library/Password/src/LibraryPassword.Codeunit.al b/src/System Application/Test Library/Password/src/LibraryPassword.Codeunit.al new file mode 100644 index 0000000000..183bb8e616 --- /dev/null +++ b/src/System Application/Test Library/Password/src/LibraryPassword.Codeunit.al @@ -0,0 +1,25 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.TestLibraries.Security.AccessControl; + +using System.Security.AccessControl; + +codeunit 132528 "Library - Password" +{ + var + PasswordDialogImpl: Codeunit "Password Dialog Impl."; + + /// + /// Opens a dialog for the user to change a password and returns the old and new typed passwords if there is no validation error, + /// otherwise an empty text are returned. Used for OnPrem report "Change Password" + /// + /// Out parameter, the old password user typed on the dialog. + /// Out parameter, the new password user typed on the dialog. + procedure OpenChangePasswordDialog(var OldPassword: SecretText; var Password: SecretText) + begin + PasswordDialogImpl.OpenChangePasswordDialog(OldPassword, Password); + end; +} \ No newline at end of file diff --git a/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al b/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al index 596ca6691b..e9d8c6175f 100644 --- a/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al +++ b/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al @@ -210,7 +210,7 @@ codeunit 135033 "Password Dialog Test" [HandlerFunctions('ChangePasswordDialogModalPageHandler')] procedure OpenChangePasswordDialogTest(); var - PasswordDialogImpl: Codeunit "Password Dialog Impl."; + LibraryPassword: Codeunit "Library - Password"; Password: SecretText; OldPassword: SecretText; begin @@ -220,7 +220,7 @@ codeunit 135033 "Password Dialog Test" PermissionsMock.Set('All Objects'); // [WHEN] The password dialog is opened in change password mode. - PasswordDialogImpl.OpenChangePasswordDialog(OldPassword, Password); + LibraryPassword.OpenChangePasswordDialog(OldPassword, Password); // [THEN] The Old and New passwords are retrieved. Assert.AreEqual(InValidPasswordTxt, GetPasswordValue(OldPassword), 'A diferrent password was expected.'); From 746e0bf6340a262043c341a0f74cf3dc21f72ef4 Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Wed, 22 May 2024 20:24:05 +0300 Subject: [PATCH 10/12] Renumber permission to correct id --- .../Password/Permissions/PasswordExec.PermissionSet.al | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System Application/Test Library/Password/Permissions/PasswordExec.PermissionSet.al b/src/System Application/Test Library/Password/Permissions/PasswordExec.PermissionSet.al index 9d1fe071cd..271729c3b7 100644 --- a/src/System Application/Test Library/Password/Permissions/PasswordExec.PermissionSet.al +++ b/src/System Application/Test Library/Password/Permissions/PasswordExec.PermissionSet.al @@ -6,7 +6,7 @@ namespace System.TestLibraries.Security.AccessControl; -permissionset 135033 "Password Exec" +permissionset 132528 "Password Exec" { Assignable = true; ObsoleteState = Pending; From 01a70b93a7001d5afdcbc381ea0d252eb2eb1c3e Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Wed, 22 May 2024 20:29:46 +0300 Subject: [PATCH 11/12] Keep original id --- .../Password/Permissions/PasswordExec.PermissionSet.al | 2 +- src/System Application/Test Library/Password/app.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/System Application/Test Library/Password/Permissions/PasswordExec.PermissionSet.al b/src/System Application/Test Library/Password/Permissions/PasswordExec.PermissionSet.al index 271729c3b7..9d1fe071cd 100644 --- a/src/System Application/Test Library/Password/Permissions/PasswordExec.PermissionSet.al +++ b/src/System Application/Test Library/Password/Permissions/PasswordExec.PermissionSet.al @@ -6,7 +6,7 @@ namespace System.TestLibraries.Security.AccessControl; -permissionset 132528 "Password Exec" +permissionset 135033 "Password Exec" { Assignable = true; ObsoleteState = Pending; diff --git a/src/System Application/Test Library/Password/app.json b/src/System Application/Test Library/Password/app.json index 76877e7b91..333fdba0c2 100644 --- a/src/System Application/Test Library/Password/app.json +++ b/src/System Application/Test Library/Password/app.json @@ -25,6 +25,10 @@ { "from": 132528, "to": 132528 + }, + { + "from": 135033, + "to": 135033 } ], "platform": "25.0.0.0", From 6bfe79b98b8bf7816f889a315d8c0f1237ef3b9d Mon Sep 17 00:00:00 2001 From: Volodymyr Dvernytskyi Date: Thu, 23 May 2024 18:37:25 +0300 Subject: [PATCH 12/12] Update tests according to new dialog password method --- .../Test/Password/src/PasswordDialogTest.Codeunit.al | 6 +++--- .../src/SystemInitializationTest.Codeunit.al | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al b/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al index e9d8c6175f..63265da1ca 100644 --- a/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al +++ b/src/System Application/Test/Password/src/PasswordDialogTest.Codeunit.al @@ -238,10 +238,10 @@ codeunit 135033 "Password Dialog Test" PermissionsMock.Set('All Objects'); // [WHEN] The password dialog is opened in change password mode. - PasswordDialogManagement.OpenChangePasswordDialog(CurrentPassword, Password); + PasswordDialogManagement.OpenPasswordChangeDialog(CurrentPassword, Password); - // [THEN] The Old and New passwords are retrieved. - Assert.AreEqual(InValidPasswordTxt, GetPasswordValue(CurrentPassword), 'A diferrent password was expected.'); + // [THEN] The New passwords was retrieved. + Assert.AreEqual('', GetPasswordValue(CurrentPassword), 'A diferrent password was expected.'); Assert.AreEqual(ValidPasswordTxt, GetPasswordValue(Password), 'A diferrent password was expected.') end; diff --git a/src/System Application/Test/System Initialization/src/SystemInitializationTest.Codeunit.al b/src/System Application/Test/System Initialization/src/SystemInitializationTest.Codeunit.al index 20188ee021..b98fb2a202 100644 --- a/src/System Application/Test/System Initialization/src/SystemInitializationTest.Codeunit.al +++ b/src/System Application/Test/System Initialization/src/SystemInitializationTest.Codeunit.al @@ -33,15 +33,15 @@ codeunit 130045 "System Initialization Test" CompanyTriggers: Codeunit "Company Triggers"; PasswordDialogManagement: Codeunit "Password Dialog Management"; PermissionsMock: Codeunit "Permissions Mock"; - OldPassword: SecretText; + CurrentPassword: SecretText; NewPassword: SecretText; begin PermissionsMock.Set('System Init Exec'); // [WHEN] Calling CompanyTriggers.OnCompanyOpen() CompanyTriggers.OnCompanyOpenCompleted(); - // [THEN] Calling PasswordDialog.OpenChangePasswordDialog should NOT results in an error - PasswordDialogManagement.OpenChangePasswordDialog(OldPassword, NewPassword); + // [THEN] Calling PasswordDialog.OpenPasswordChangeDialog should NOT results in an error + PasswordDialogManagement.OpenPasswordChangeDialog(CurrentPassword, NewPassword); end; [ModalPageHandler]