diff --git a/AyCode.Database/DataLayers/Users/AcUserDalBase.cs b/AyCode.Database/DataLayers/Users/AcUserDalBase.cs index 71bd558..49777b3 100644 --- a/AyCode.Database/DataLayers/Users/AcUserDalBase.cs +++ b/AyCode.Database/DataLayers/Users/AcUserDalBase.cs @@ -59,6 +59,7 @@ namespace AyCode.Database.DataLayers.Users public virtual Task UpdateUserAsync(TUser user) => UpdateSafeAsync(user); + public virtual TUser? UpdateUser(TUser user) => UpdateSafe(user); public Task AddUserAsync(TUser user) { diff --git a/AyCode.Interfaces/Logins/IAcLoginServiceCommon.cs b/AyCode.Interfaces/Logins/IAcLoginServiceCommon.cs index 235256b..d1a359d 100644 --- a/AyCode.Interfaces/Logins/IAcLoginServiceCommon.cs +++ b/AyCode.Interfaces/Logins/IAcLoginServiceCommon.cs @@ -20,4 +20,7 @@ public interface IAcLoginServiceCommon RegistrationAsync(string email, string password, string? phoneNumber = null, Guid? referralId = null); public Task RegistrationAsync(Guid userId, string email, string password, string? phoneNumber = null, Guid? referralId = null); + + public AcErrorCode ChangePassword(Guid userId, string oldPassword, string newPassword); + public Task ChangePasswordAsync(Guid userId, string oldPassword, string newPassword); } \ No newline at end of file diff --git a/AyCode.Models/Users/AcChangePasswordDto.cs b/AyCode.Models/Users/AcChangePasswordDto.cs new file mode 100644 index 0000000..1812714 --- /dev/null +++ b/AyCode.Models/Users/AcChangePasswordDto.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AyCode.Models.Users +{ + public abstract class AcChangePasswordDto + { + public Guid UserId { get; set; } + public string OldPassword { get; set; } + public string NewPassword { get; set; } + + protected AcChangePasswordDto() { } + + protected AcChangePasswordDto(Guid userId, string oldPassword, string newPassword) : this() + { + UserId = userId; + OldPassword = oldPassword; + NewPassword = newPassword; + } + } +} diff --git a/AyCode.Services.Server.Tests/LoginServices/AcLoginServiceServerTestBase.cs b/AyCode.Services.Server.Tests/LoginServices/AcLoginServiceServerTestBase.cs index 064276a..0b55c2c 100644 --- a/AyCode.Services.Server.Tests/LoginServices/AcLoginServiceServerTestBase.cs +++ b/AyCode.Services.Server.Tests/LoginServices/AcLoginServiceServerTestBase.cs @@ -12,14 +12,15 @@ using AyCode.Core.Consts; using AyCode.Core.Extensions; using AyCode.Services.Server.Logins; using AyCode.Utils.Extensions; +using AyCode.Core.Helpers; namespace AyCode.Services.Server.Tests.LoginServices { public abstract class AcLoginServiceServerTestBase : AcDatabaseTestModelBase where TDal : AcUserDalBase - where TDbContext : AcDbContextBase, IAcUserDbContextBase + where TDbContext : AcDbContextBase, IAcUserDbContextBase where TLoginServiceServer : class, IAcLoginServiceServer - where TResultLoggedInModel: class, IAcLoggedInModelBase + where TResultLoggedInModel : class, IAcLoggedInModelBase where TUser : class, IAcUser where TProfile : class, IAcProfile where TProfileAddress : class, IAcAddress @@ -42,11 +43,11 @@ namespace AyCode.Services.Server.Tests.LoginServices Assert.IsNotNull(loginService); var errorCode = await loginService.RegistrationAsync(registerUserId, registerEmail, registerPassword, null); - + Assert.IsTrue(errorCode == AcErrorCode.Unset); var user = Dal.GetUserByEmail(registerEmail, false); - + Assert.IsNotNull(user); Assert.IsNotNull(user.Profile); Assert.IsNotNull(user.Profile.Address); @@ -65,7 +66,8 @@ namespace AyCode.Services.Server.Tests.LoginServices Assert.IsNotNull(loginService); #region Valid email+password test - var loggedInModel = loginService.Login(loginEmail, loginPassword); + + var loggedInModel = loginService.Login(loginEmail, loginPassword); Assert.IsNotNull(loggedInModel); Assert.IsNotNull(loggedInModel.LoggedInUser); @@ -74,23 +76,57 @@ namespace AyCode.Services.Server.Tests.LoginServices Assert.IsTrue(loggedInModel.LoginErrorCode == AcErrorCode.Unset, $"errorCode: {loggedInModel.LoginErrorCode}"); Assert.IsTrue(loggedInModel.IsLoggedIn, $"loggedInModel.IsLoggedIn == false; errorCode: {loggedInModel.LoginErrorCode}"); Assert.IsTrue(string.Equals(loggedInModel.LoggedInUser.EmailAddress, loginEmail, StringComparison.CurrentCultureIgnoreCase)); + #endregion Valid email+password test #region Wrong email test + loggedInModel = loginService.Login("gffsdgdfg@gu.hu", loginPassword); Assert.IsNotNull(loggedInModel); Assert.IsFalse(loggedInModel.IsLoggedIn); Assert.IsTrue(loggedInModel.LoginErrorCode == AcErrorCode.WrongLoginData); + #endregion Wrong email test #region Wrong password test + loggedInModel = loginService.Login(loginEmail, "fsdgfsdg"); Assert.IsNotNull(loggedInModel); Assert.IsFalse(loggedInModel.IsLoggedIn); Assert.IsTrue(loggedInModel.LoginErrorCode == AcErrorCode.WrongLoginData); + #endregion Wrong password test } + + [DataTestMethod] + [DataRow(["", "", "", ""])] + public virtual void AcBase_ChangePassword_ReturnUser_WhenUserLoggedInWithNewPassword(string[] userIdOldPasswordNewPasswordDbBackupHashStrings) + { + var userId = Guid.Parse(userIdOldPasswordNewPasswordDbBackupHashStrings[0]); + var oldPassword = userIdOldPasswordNewPasswordDbBackupHashStrings[1]; + var newPassword = userIdOldPasswordNewPasswordDbBackupHashStrings[2]; + var oldPasswordBackupHash = userIdOldPasswordNewPasswordDbBackupHashStrings[3]; + + var user = Dal.GetUserById(userId, false)!; + + //Visszaállítjuk az eredeti jelszót... - J. + if (!PasswordHasher.VerifyPassword(oldPassword, user.Password, PasswordHasher.GenerateDynamicSalt(userId))) + { + user.Password = oldPasswordBackupHash; + Dal.UpdateUser(user); + } + + var loginService = Activator.CreateInstance(typeof(TLoginServiceServer), Dal, AcEnv.AppConfiguration) as TLoginServiceServer; + Assert.IsNotNull(loginService); + + var errorCode = loginService.ChangePassword(userId, oldPassword, newPassword); + Assert.IsTrue(errorCode == AcErrorCode.Unset, $"{errorCode}"); + + var loggedInModel = loginService.Login(user.EmailAddress, newPassword); + Assert.IsNotNull(loggedInModel); + Assert.IsTrue(loggedInModel.IsLoggedIn); + } } } \ No newline at end of file diff --git a/AyCode.Services.Server/Logins/AcLoginServiceServer.cs b/AyCode.Services.Server/Logins/AcLoginServiceServer.cs index 2e63955..953eaf6 100644 --- a/AyCode.Services.Server/Logins/AcLoginServiceServer.cs +++ b/AyCode.Services.Server/Logins/AcLoginServiceServer.cs @@ -98,8 +98,8 @@ public class AcLoginServiceServer(); address.Id = Guid.NewGuid(); @@ -117,6 +117,33 @@ public class AcLoginServiceServer RegistrationAsync(Guid userId, string email, string password, string? phoneNumber = null, Guid? referralId = null) => TaskHelper.ToThreadPoolTask(() => Registration(userId, email, password, phoneNumber, referralId)); + public virtual AcErrorCode ChangePassword(Guid userId, string oldPassword, string newPassword) + { + try + { + if (userId.IsNullOrEmpty()) return AcErrorCode.IdIsNullOrEmpty; + if (!AcValidate.IsValidPasswordFormat(newPassword, out var errorCode)) return errorCode; + + var user = userDal.GetUserById(userId, false); //TODO: csak az EmailConfirmed user password-öket lehessen change-elni! - J. + if (user == null) return AcErrorCode.EntityIsNull; + + if (!PasswordHasher.VerifyPassword(oldPassword, user.Password, PasswordHasher.GenerateDynamicSalt(user.Id))) return AcErrorCode.WrongLoginData; + + user.Password = PasswordHasher.HashPassword(newPassword, PasswordHasher.GenerateDynamicSalt(userId)); + + return userDal.UpdateUser(user) == null ? AcErrorCode.DatabaseError : AcErrorCode.Unset; + } + catch (Exception) + { + // ignored + } + + return AcErrorCode.UnknownError; + } + + public virtual Task ChangePasswordAsync(Guid userId, string oldPassword, string newPassword) + => TaskHelper.ToThreadPoolTask(() => ChangePassword(userId, oldPassword, newPassword)); + public virtual bool SendConfirmationToken(string? email, string confirmationToken) { //var sendGrid = SendGrid.SendGridClient(); diff --git a/AyCode.Services/Logins/AcLoginServiceClient.cs b/AyCode.Services/Logins/AcLoginServiceClient.cs index 5e82ae4..d1878ca 100644 --- a/AyCode.Services/Logins/AcLoginServiceClient.cs +++ b/AyCode.Services/Logins/AcLoginServiceClient.cs @@ -8,7 +8,7 @@ using AyCode.Interfaces.Users; namespace AyCode.Services.Logins; -public class AcLoginServiceClient +public class AcLoginServiceClient : AcLoginServiceBase, IAcLoginServiceClient where TUser : class, IAcUser @@ -42,7 +42,7 @@ public class AcLoginServiceClient Registration(email, password, phoneNumber)); } - public Task RegistrationAsync(Guid userId, string email, string password, string? phoneNumber = null, Guid? referralId = null) + public virtual Task RegistrationAsync(Guid userId, string email, string password, string? phoneNumber = null, Guid? referralId = null) + { + throw new NotImplementedException(); + } + + public virtual AcErrorCode ChangePassword(Guid userId, string oldPassword, string newPassword) + { + throw new NotImplementedException(); + } + + public virtual Task ChangePasswordAsync(Guid userId, string oldPassword, string newPassword) { throw new NotImplementedException(); }