diff --git a/TIAM.Services/Interfaces/IUserApiControllerClient.cs b/TIAM.Services/Interfaces/IUserApiControllerClient.cs index dbeb6ff8..18a651c3 100644 --- a/TIAM.Services/Interfaces/IUserApiControllerClient.cs +++ b/TIAM.Services/Interfaces/IUserApiControllerClient.cs @@ -1,5 +1,9 @@ -namespace TIAM.Services.Interfaces; +using AyCode.Core.Consts; + +namespace TIAM.Services.Interfaces; public interface IUserApiControllerClient : IUserApiControllerCommon { + public Task UserChangePassword(Guid userId, string oldPassword, string newPassword); + public Task UserForgotPassword(string email, string newPassword); } \ No newline at end of file diff --git a/TIAM.Services/Interfaces/IUserApiControllerCommon.cs b/TIAM.Services/Interfaces/IUserApiControllerCommon.cs index 7a56b0b6..fb3c9312 100644 --- a/TIAM.Services/Interfaces/IUserApiControllerCommon.cs +++ b/TIAM.Services/Interfaces/IUserApiControllerCommon.cs @@ -1,4 +1,5 @@ -using TIAM.Entities.Users; +using AyCode.Core.Consts; +using TIAM.Entities.Users; using TIAM.Models.Dtos.Users; namespace TIAM.Services.Interfaces; @@ -7,4 +8,7 @@ public interface IUserApiControllerCommon { public Task UpdateUser(User user); public Task UpdateUserModelDtoDetail(UserModelDtoDetail userModelDtoDetail); + + public Task UserChangePassword(ChangePasswordDto changePasswordDto); + public Task UserForgotPassword(ForgotPasswordDto forgotPasswordDto); } \ No newline at end of file diff --git a/TIAMSharedUI/Pages/User/CardComponents/UserCardComponent.razor b/TIAMSharedUI/Pages/User/CardComponents/UserCardComponent.razor index 23525980..c0f3af3b 100644 --- a/TIAMSharedUI/Pages/User/CardComponents/UserCardComponent.razor +++ b/TIAMSharedUI/Pages/User/CardComponents/UserCardComponent.razor @@ -1,4 +1,5 @@ -@using BlazorAnimation +@using AyCode.Core.Consts +@using BlazorAnimation @using TIAM.Core.Enums @using TIAM.Entities.Transfers @using TIAM.Entities.Users @@ -65,7 +66,7 @@ CssClass="form-field" />
- Save + Save

@msg

@@ -111,7 +112,7 @@ } string msg; - private bool isSaveActive = false; + private bool _isSaveActive = false; void OnPasswordSet(string password) { @@ -126,35 +127,29 @@ if (NewPassword == ConfirmNewPassword) { PasswordNotConfirmed = false; - isSaveActive = true; + _isSaveActive = true; } else { - isSaveActive = false; + _isSaveActive = false; msg = "Password and confirmation not matching!"; } } protected async Task SetPassword() { - bool isSuccess; - isSaveActive = false; + _isSaveActive = false; - if (IsForgotten) - { - var forgotPasswordDto = new ForgotPasswordDto(Context.UserDto.EmailAddress, NewPassword); - isSuccess = await AdminSignalRClient.PostDataAsync(SignalRTags.UserForgotPassword, forgotPasswordDto) != null; - } - else - { - var changePasswordDto = new ChangePasswordDto(Context.Id, OldPassword, NewPassword); - isSuccess = await AdminSignalRClient.PostDataAsync(SignalRTags.UserChangePassword, changePasswordDto) != null; - } + var errorCode = IsForgotten ? + await AdminSignalRClient.UserForgotPassword(Context.UserDto.EmailAddress, NewPassword) : + await AdminSignalRClient.UserChangePassword(Context.Id, OldPassword, NewPassword); - msg = isSuccess ? $"Password saved" : "Some error occured during saving, please try again later"; + var isSucces = errorCode == AcErrorCode.Unset; + + msg = isSucces ? "Password saved" : $"Some error occured during saving, please try again later! [{errorCode}]"; StateHasChanged(); - await DataChanged.InvokeAsync(isSuccess); + await DataChanged.InvokeAsync(isSucces); } protected override async Task OnInitializedAsync() diff --git a/TIAMWebApp/Server/Controllers/UserAPIController.cs b/TIAMWebApp/Server/Controllers/UserAPIController.cs index 1503e0ca..48055be5 100644 --- a/TIAMWebApp/Server/Controllers/UserAPIController.cs +++ b/TIAMWebApp/Server/Controllers/UserAPIController.cs @@ -12,35 +12,30 @@ using AyCode.Core.Loggers; using TIAM.Database.DataLayers.Users; using TIAM.Entities.Users; using TIAM.Models.Dtos.Users; -using TIAM.Entities.Profiles; -using TIAM.Entities.Addresses; -using TIAM.Services.Server.Logins; -using ILogger = TIAM.Core.Loggers.ILogger; using AyCode.Core.Helpers; -using AyCode.Entities; using AyCode.Services.SignalRs; -using TIAM.Models.Server.Logins; using TIAM.Services; using TIAM.Services.Interfaces; -using TIAMWebApp.Shared.Application.Services; -using GoogleApi.Entities.Search.Video.Common; using TIAM.Database.DataLayers.Admins; using TIAM.Entities.Emails; using TIAMWebApp.Shared.Application.Models.ClientSide.Messages; using TIAMWebApp.Shared.Application.Models.ClientSide; using System.Net; using TIAM.Services.Server; +using TIAM.Services.Server.Logins; +using TIAMWebApp.Server.Services; namespace TIAMWebApp.Server.Controllers { [Authorize] [ApiController] [Route("api/v1/[controller]")] - public class UserAPIController(IConfiguration configuration, AdminDal adminDal, UserDal userDal, IMessageSenderService messageSenderService, IEnumerable logWriters) : ControllerBase, IUserApiControllerCommon + public class UserAPIController(IConfiguration configuration, AdminDal adminDal, UserDal userDal, IMessageSenderService messageSenderService, SessionService sessionService, IEnumerable logWriters) : ControllerBase, IUserApiControllerCommon { private readonly TIAM.Core.Loggers.Logger _logger = new(logWriters.ToArray()); - private LoginService _loginService = new LoginService(userDal, configuration); + private readonly LoginService _loginService = new(userDal, configuration); + //private readonly IWebHostEnvironment _webHostEnvironment; //readonly PasswordHasher _hasher = new(); @@ -348,32 +343,30 @@ namespace TIAMWebApp.Server.Controllers [NonAction] [SignalR(SignalRTags.UserChangePassword)] - public async Task UserChangePassword([FromBody] ChangePasswordDto changePasswordDto) + public async Task UserChangePassword([FromBody] ChangePasswordDto changePasswordDto) { _logger.Info("ChangeUserPassword called"); var errorCode = await _loginService.ChangePasswordAsync(changePasswordDto.UserId, changePasswordDto.OldPassword, changePasswordDto.NewPassword); - if (errorCode == AcErrorCode.Unset) - return await userDal.GetUserModelDtoByIdAsync(changePasswordDto.UserId, true); + if (errorCode != AcErrorCode.Unset) + _logger.Error($"ErrorCode: {errorCode}; userId: {changePasswordDto.UserId}"); - _logger.Error($"ErrorCode: {errorCode}; userId: {changePasswordDto.UserId}"); - return null; + return errorCode; } [NonAction] [SignalR(SignalRTags.UserForgotPassword)] - public async Task UserForgotPassword([FromBody] ForgotPasswordDto forgotPasswordDto) + public async Task UserForgotPassword([FromBody] ForgotPasswordDto forgotPasswordDto) { _logger.Info("UserForgotPassword called"); var errorCode = await _loginService.ForgotPasswordAsync(forgotPasswordDto.Email, forgotPasswordDto.NewPassword); - if (errorCode == AcErrorCode.Unset) - return await userDal.GetUserModelDtoByEmailAsync(forgotPasswordDto.Email, true); + if (errorCode != AcErrorCode.Unset) + _logger.Error($"ErrorCode: {errorCode}; email: {forgotPasswordDto.Email}"); - _logger.Error($"ErrorCode: {errorCode}; email: {forgotPasswordDto.Email}"); - return null; + return errorCode; } [NonAction] diff --git a/TIAMWebApp/Server/Program.cs b/TIAMWebApp/Server/Program.cs index a810b381..10d16090 100644 --- a/TIAMWebApp/Server/Program.cs +++ b/TIAMWebApp/Server/Program.cs @@ -35,6 +35,7 @@ builder.Services.AddServerSideBlazor(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/TIAMWebApp/Server/Services/DevAdminSignalRhub.cs b/TIAMWebApp/Server/Services/DevAdminSignalRhub.cs index e026cefa..ad3384a0 100644 --- a/TIAMWebApp/Server/Services/DevAdminSignalRhub.cs +++ b/TIAMWebApp/Server/Services/DevAdminSignalRhub.cs @@ -1,6 +1,4 @@ -using System.Collections.Concurrent; -using System.Reflection; -using AyCode.Core.Extensions; +using AyCode.Core.Extensions; using AyCode.Core.Loggers; using AyCode.Services.SignalRs; using Microsoft.AspNetCore.SignalR; @@ -9,107 +7,46 @@ using MessagePack.Resolvers; using AyCode.Services.Server.SignalRs; using TIAM.Services; using TIAMWebApp.Server.Controllers; -using TIAM.Entities.ServiceProviders; -using System.Runtime.CompilerServices; using MessagePack; using TIAM.Entities.Addresses; -using Microsoft.AspNetCore.Hosting; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; using System.Linq.Expressions; -using AutoMapper; using AyCode.Core.Helpers; -using DevExpress.Pdf.Native.BouncyCastle.Security; -using TIAM.Entities.Emails; -using TIAM.Services.Server; using Profile = TIAM.Entities.Profiles.Profile; using Serialize.Linq.Serializers; using System.Security.Claims; using AyCode.Core; +using AyCode.Blazor.Components.Services; +using TIAM.Database.DataLayers.Users; +using TIAM.Services.Server.Logins; +using TIAMWebApp.Shared.Application.Interfaces; namespace TIAMWebApp.Server.Services; -public static class ExtensionMethods -{ - public static object? InvokeMethod(this MethodInfo methodInfo, object obj, params object[]? parameters) - { - if (methodInfo.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) is AsyncStateMachineAttribute isAsyncTask) - { - dynamic awaitable = methodInfo.Invoke(obj, parameters)!; - return awaitable.GetAwaiter().GetResult(); - } - - return methodInfo.Invoke(obj, parameters); - } -} - -public class MethodInfoModel where TAttribute : TagAttribute -{ - public ParameterInfo[]? ParamInfos { get; init; } = null; - public TAttribute Attribute { get; init; } - public MethodInfo MethodInfo { get; init; } - - public MethodInfoModel(TAttribute attribute, MethodInfo methodInfo) - { - Attribute = attribute; - MethodInfo = methodInfo; - - var parameters = methodInfo.GetParameters(); - - //if (parameters.Length > 1) - // throw new Exception("MethodInfoModel; parameters.Length > 1"); - - ParamInfos = parameters; - } -} - -public class DynamicMethodCallModel where TAttribute : TagAttribute -{ - public object InstanceObject { get; init; } - public ConcurrentDictionary> MethodsByMessageTag { get; init; } = new(); - - - public DynamicMethodCallModel(Type instanceObjectType) : this(instanceObjectType, null!) - { - } - - public DynamicMethodCallModel(Type instanceObjectType, params object[] constructorParams) : this(Activator.CreateInstance(instanceObjectType, constructorParams)!) - { - } - - public DynamicMethodCallModel(object instanceObject) - { - InstanceObject = instanceObject; - - foreach (var methodInfo in instanceObject.GetType().GetMethods()) - { - if (methodInfo.GetCustomAttribute(typeof(TAttribute)) is not TAttribute attribute) continue; - - if (MethodsByMessageTag.ContainsKey(attribute.MessageTag)) - throw new Exception($"Multiple SignaRMessageTag! messageTag: {attribute.MessageTag}; methodName: {methodInfo.Name}"); - - MethodsByMessageTag[attribute.MessageTag] = new MethodInfoModel(attribute, methodInfo!); - } - } -} - public class DevAdminSignalRHub : Hub, IAcSignalRHubServer { private readonly List> _dynamicMethodCallModels = []; private readonly TIAM.Core.Loggers.Logger _logger; + + private SessionService _sessionService; + private IConfiguration _configuration; private readonly AdminDal _adminDal; + private readonly UserDal _userDal; //private readonly ServiceProviderAPIController _serviceProviderApiController; //private readonly TransferDataAPIController _transferDataApiController; - public DevAdminSignalRHub(AdminDal adminDal, UserAPIController userApiController, ServiceProviderAPIController serviceProviderApiController, TransferDataAPIController transferDataApiController, MessageAPIController messageApiController, ProfileAPIController profileApiController, LoggerApiController loggerApiController, IEnumerable logWriters) + public DevAdminSignalRHub(IConfiguration configuration, AdminDal adminDal, UserDal userDal, UserAPIController userApiController, ServiceProviderAPIController serviceProviderApiController, TransferDataAPIController transferDataApiController, MessageAPIController messageApiController, ProfileAPIController profileApiController, LoggerApiController loggerApiController, SessionService sessionService, IEnumerable logWriters) { _adminDal = adminDal; + _userDal = userDal; + + _configuration = configuration; + //_serviceProviderApiController = serviceProviderApiController; //_transferDataApiController = transferDataApiController; _logger = new(logWriters.ToArray()); + _sessionService = sessionService; _dynamicMethodCallModels.Add(new DynamicMethodCallModel(userApiController)); _dynamicMethodCallModels.Add(new DynamicMethodCallModel(serviceProviderApiController)); @@ -128,6 +65,9 @@ public class DevAdminSignalRHub : Hub, IAcSignalRHubServe LogContextUserNameAndId(); + _sessionService.Sessions.TryAdd(Context.ConnectionId, new SessionItem(Context.ConnectionId, new LoginService(_userDal, _configuration))); + _logger.Info($"_sessionService.Sessions count: {_sessionService.Sessions.Count}"); + ////insert or updatde them into database. //var CId = _context.UserIdToCId.Find(userId); //CId.ConnectionId = connectionid; @@ -151,6 +91,9 @@ public class DevAdminSignalRHub : Hub, IAcSignalRHubServe LogContextUserNameAndId(); + if (_sessionService.Sessions.TryRemove(Context.ConnectionId, out var sessionItem)) sessionItem.LoginService.Logout(); + _logger.Info($"_sessionService.Sessions count: {_sessionService.Sessions.Count}"); + //await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users"); await base.OnDisconnectedAsync(exception); } diff --git a/TIAMWebApp/Server/Services/DynamicMethodCallModel.cs b/TIAMWebApp/Server/Services/DynamicMethodCallModel.cs new file mode 100644 index 00000000..67e5e11b --- /dev/null +++ b/TIAMWebApp/Server/Services/DynamicMethodCallModel.cs @@ -0,0 +1,35 @@ +using System.Collections.Concurrent; +using System.Reflection; +using AyCode.Services.SignalRs; + +namespace TIAMWebApp.Server.Services; + +public class DynamicMethodCallModel where TAttribute : TagAttribute +{ + public object InstanceObject { get; init; } + public ConcurrentDictionary> MethodsByMessageTag { get; init; } = new(); + + + public DynamicMethodCallModel(Type instanceObjectType) : this(instanceObjectType, null!) + { + } + + public DynamicMethodCallModel(Type instanceObjectType, params object[] constructorParams) : this(Activator.CreateInstance(instanceObjectType, constructorParams)!) + { + } + + public DynamicMethodCallModel(object instanceObject) + { + InstanceObject = instanceObject; + + foreach (var methodInfo in instanceObject.GetType().GetMethods()) + { + if (methodInfo.GetCustomAttribute(typeof(TAttribute)) is not TAttribute attribute) continue; + + if (MethodsByMessageTag.ContainsKey(attribute.MessageTag)) + throw new Exception($"Multiple SignaRMessageTag! messageTag: {attribute.MessageTag}; methodName: {methodInfo.Name}"); + + MethodsByMessageTag[attribute.MessageTag] = new MethodInfoModel(attribute, methodInfo!); + } + } +} \ No newline at end of file diff --git a/TIAMWebApp/Server/Services/ExtensionMethods.cs b/TIAMWebApp/Server/Services/ExtensionMethods.cs new file mode 100644 index 00000000..82746e71 --- /dev/null +++ b/TIAMWebApp/Server/Services/ExtensionMethods.cs @@ -0,0 +1,18 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace TIAMWebApp.Server.Services; + +public static class ExtensionMethods +{ + public static object? InvokeMethod(this MethodInfo methodInfo, object obj, params object[]? parameters) + { + if (methodInfo.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) is AsyncStateMachineAttribute isAsyncTask) + { + dynamic awaitable = methodInfo.Invoke(obj, parameters)!; + return awaitable.GetAwaiter().GetResult(); + } + + return methodInfo.Invoke(obj, parameters); + } +} \ No newline at end of file diff --git a/TIAMWebApp/Server/Services/MethodInfoModel.cs b/TIAMWebApp/Server/Services/MethodInfoModel.cs new file mode 100644 index 00000000..decf6fd3 --- /dev/null +++ b/TIAMWebApp/Server/Services/MethodInfoModel.cs @@ -0,0 +1,24 @@ +using System.Reflection; +using AyCode.Services.SignalRs; + +namespace TIAMWebApp.Server.Services; + +public class MethodInfoModel where TAttribute : TagAttribute +{ + public ParameterInfo[]? ParamInfos { get; init; } = null; + public TAttribute Attribute { get; init; } + public MethodInfo MethodInfo { get; init; } + + public MethodInfoModel(TAttribute attribute, MethodInfo methodInfo) + { + Attribute = attribute; + MethodInfo = methodInfo; + + var parameters = methodInfo.GetParameters(); + + //if (parameters.Length > 1) + // throw new Exception("MethodInfoModel; parameters.Length > 1"); + + ParamInfos = parameters; + } +} \ No newline at end of file diff --git a/TIAMWebApp/Server/Services/SessionItem.cs b/TIAMWebApp/Server/Services/SessionItem.cs new file mode 100644 index 00000000..d7663e2f --- /dev/null +++ b/TIAMWebApp/Server/Services/SessionItem.cs @@ -0,0 +1,16 @@ +using AyCode.Blazor.Components.Services; +using TIAM.Services.Server.Logins; + +namespace TIAMWebApp.Server.Services; + +public class SessionItem : IAcSessionItem +{ + public string SessionId { get; set; } + public LoginService LoginService { get; set; } + + public SessionItem(string sessionId, LoginService loginService) + { + SessionId = sessionId; + LoginService = loginService; + } +} \ No newline at end of file diff --git a/TIAMWebApp/Server/Services/SessionService.cs b/TIAMWebApp/Server/Services/SessionService.cs new file mode 100644 index 00000000..2b6c2dc4 --- /dev/null +++ b/TIAMWebApp/Server/Services/SessionService.cs @@ -0,0 +1,8 @@ +using AyCode.Blazor.Components.Services; + +namespace TIAMWebApp.Server.Services; + +public class SessionService : AcSessionService +{ + +} \ No newline at end of file diff --git a/TIAMWebApp/Shared/Services/AdminSignalRClient.cs b/TIAMWebApp/Shared/Services/AdminSignalRClient.cs index 36e90108..09889a38 100644 --- a/TIAMWebApp/Shared/Services/AdminSignalRClient.cs +++ b/TIAMWebApp/Shared/Services/AdminSignalRClient.cs @@ -1,15 +1,8 @@ -using System.Collections.Concurrent; -using AyCode.Blazor.Components.Services; -using AyCode.Core; -using AyCode.Core.Extensions; +using AyCode.Blazor.Components.Services; +using AyCode.Core.Consts; using AyCode.Core.Helpers; -using AyCode.Core.Interfaces; -using AyCode.Interfaces.Entities; using AyCode.Services.Loggers; using AyCode.Services.SignalRs; -using MessagePack.Resolvers; -using Microsoft.AspNetCore.SignalR.Client; -using TIAM.Core.Consts; using TIAM.Entities.Drivers; using TIAM.Entities.Transfers; using TIAM.Entities.Users; @@ -34,6 +27,13 @@ namespace TIAMWebApp.Shared.Application.Services public async Task UpdateUserModelDtoDetail(UserModelDtoDetail userModelDtoDetail) => await PostDataAsync(SignalRTags.UpdateUserModelDtoDetail, userModelDtoDetail); + + public Task UserChangePassword(Guid userId, string oldPassword, string newPassword) => UserChangePassword(new ChangePasswordDto(userId, oldPassword, newPassword)); + public Task UserChangePassword(ChangePasswordDto changePasswordDto) => PostDataAsync(SignalRTags.UserChangePassword, changePasswordDto); + + public Task UserForgotPassword(string email, string newPassword) => UserForgotPassword(new ForgotPasswordDto(email, newPassword)); + public Task UserForgotPassword(ForgotPasswordDto forgotPasswordDto) => PostDataAsync(SignalRTags.UserForgotPassword, forgotPasswordDto); + #endregion IUserApiController #region ICompanyApiController diff --git a/Tiam.Services.Client.Tests/SignalRClientTest.cs b/Tiam.Services.Client.Tests/SignalRClientTest.cs index fdce0421..95b68ead 100644 --- a/Tiam.Services.Client.Tests/SignalRClientTest.cs +++ b/Tiam.Services.Client.Tests/SignalRClientTest.cs @@ -1,3 +1,4 @@ +using AyCode.Core.Consts; using AyCode.Core.Enums; using AyCode.Core.Helpers; using AyCode.Core.Loggers; @@ -17,6 +18,7 @@ using TIAM.Entities.Drivers; using TIAM.Entities.ServiceProviders; using TIAM.Entities.Transfers; using TIAM.Entities.Users; +using TIAM.Models.Dtos.Users; using TIAM.Services; using TIAMWebApp.Shared.Application.Services; using TIAMWebApp.Shared.Application.Utility; @@ -256,14 +258,40 @@ namespace Tiam.Services.Client.Tests Assert.IsTrue(transfers.All(x => x.UserId == userId)); } + [TestMethod] + [DataRow("540271F6-C604-4C16-8160-D5A7CAFEDF00")] + public async Task ChangePasswordTest_ReturnErrorCode_WhenErrorCodeIsUnset(string userIdString) + { + var userId = Guid.Parse(userIdString); + var errorCode = await _signalRClient.UserChangePassword(userId, "Asdasd123456", "Asdasd123456"); + + Assert.IsTrue(errorCode == AcErrorCode.Unset, errorCode.ToString()); + } + + [TestMethod] + [DataRow("540271F6-C604-4C16-8160-D5A7CAFEDF00")] + public async Task ChangePasswordTest_ReturnErrorCode_WhenErrorCodeIsNotUnset(string userIdString) + { + var userId = Guid.Parse(userIdString); + + var changePasswordDto = new ChangePasswordDto(userId, "ffdsdergtyf22A", "Asdasd123456"); + + var errorCode = await _signalRClient.UserChangePassword(changePasswordDto); + Assert.IsTrue(errorCode == AcErrorCode.WrongLoginData, errorCode.ToString()); + + changePasswordDto.UserId = Guid.Parse("110271F6-C604-4C16-8160-D5A7CAFEDF11"); + errorCode = await _signalRClient.UserChangePassword(changePasswordDto); + Assert.IsTrue(errorCode == AcErrorCode.EntityIsNull, errorCode.ToString()); + + changePasswordDto.NewPassword = "a"; + errorCode = await _signalRClient.UserChangePassword(changePasswordDto); + Assert.IsTrue(errorCode == AcErrorCode.PasswordIsTooShort, errorCode.ToString()); + } + [TestMethod] [DataRow("")] public async Task GetAllCarsAndDriversByProductIdAsyncTest_ReturnCarsAndDrivers_WhenHasCarsAndDrivers(string productIdString) { - //var json = JsonConvert.SerializeObject(5); - //var number = JsonConvert.DeserializeObject(json); - //Assert.IsTrue(number == 5); - var cars = new List(); var drivers = new List(); var productId = TiamConstClient.TransferProductId; //Guid.Parse(productIdString);