diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/ChangePasswordInput.cs b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/ChangePasswordInput.cs index 6c83a45063..fd08f8509a 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/ChangePasswordInput.cs +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/ChangePasswordInput.cs @@ -1,11 +1,16 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using Volo.Abp.Account.Localization; using Volo.Abp.Auditing; using Volo.Abp.Identity; using Volo.Abp.Validation; namespace Volo.Abp.Account; -public class ChangePasswordInput +public class ChangePasswordInput : IValidatableObject { [DisableAuditing] [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] @@ -15,4 +20,17 @@ public class ChangePasswordInput [DisableAuditing] [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))] public string NewPassword { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) + { + if (CurrentPassword == NewPassword) + { + var localizer = validationContext.GetRequiredService>(); + + yield return new ValidationResult( + localizer["NewPasswordSameAsOld"], + new[] { nameof(CurrentPassword), nameof(NewPassword) } + ); + } + } } diff --git a/modules/account/src/Volo.Abp.Account.Blazor/Pages/Account/AccountManage.razor.cs b/modules/account/src/Volo.Abp.Account.Blazor/Pages/Account/AccountManage.razor.cs index 567363843b..1aef6551c2 100644 --- a/modules/account/src/Volo.Abp.Account.Blazor/Pages/Account/AccountManage.razor.cs +++ b/modules/account/src/Volo.Abp.Account.Blazor/Pages/Account/AccountManage.razor.cs @@ -48,6 +48,12 @@ public partial class AccountManage return; } + if (ChangePasswordModel.CurrentPassword == ChangePasswordModel.NewPassword) + { + await UiMessageService.Warn(L["NewPasswordSameAsOld"]); + return; + } + await ProfileAppService.ChangePasswordAsync(new ChangePasswordInput { CurrentPassword = ChangePasswordModel.CurrentPassword, diff --git a/modules/account/test/Volo.Abp.Account.Application.Tests/Volo/Abp/Account/ProfileAppService_Tests.cs b/modules/account/test/Volo.Abp.Account.Application.Tests/Volo/Abp/Account/ProfileAppService_Tests.cs index 202844357e..d5536c9ad9 100644 --- a/modules/account/test/Volo.Abp.Account.Application.Tests/Volo/Abp/Account/ProfileAppService_Tests.cs +++ b/modules/account/test/Volo.Abp.Account.Application.Tests/Volo/Abp/Account/ProfileAppService_Tests.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using NSubstitute; using Shouldly; using Volo.Abp.Users; +using Volo.Abp.Validation; using Xunit; namespace Volo.Abp.Account; @@ -72,6 +73,27 @@ public class ProfileAppService_Tests : AbpAccountApplicationTestBase result.Name.ShouldBe(input.Name); } + [Fact] + public async Task ChangePasswordAsync_FailsForSamePassword() + { + //Arrange + _currentUser.Id.Returns(_testData.UserJohnId); + _currentUser.IsAuthenticated.Returns(true); + + //Act + var ex = await _profileAppService.ChangePasswordAsync(new() + { + CurrentPassword = "SomePassword123!", + NewPassword = "SomePassword123!" + }).ShouldThrowAsync(); + + //Assert + ex.ValidationErrors.ShouldNotBeEmpty(); + var firstError = ex.ValidationErrors[0]; + firstError.MemberNames.ShouldContain(nameof(ChangePasswordInput.CurrentPassword)); + firstError.MemberNames.ShouldContain(nameof(ChangePasswordInput.NewPassword)); + } + private static string CreateRandomEmail() { return CreateRandomString() + "@abp.io";