diff --git a/src/WorkFlowCheck.API.sln b/src/WorkFlowCheck.API.sln index 0782e62..50881b9 100644 --- a/src/WorkFlowCheck.API.sln +++ b/src/WorkFlowCheck.API.sln @@ -7,12 +7,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlowCheck.API", "WorkFl EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.NFC", "Plugin.NFC\Plugin.NFC.csproj", "{6B46795C-DF5A-4F12-95A9-691BC1741AB8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlowCheck.MAUI", "WorkFlowCheck.MAUI\WorkFlowCheck.MAUI.csproj", "{0E2BD120-EE91-4A24-A78A-CE7CA87E09AE}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlowCheck.DL", "WorkFlowCheck.DL\WorkFlowCheck.DL.csproj", "{25A8C0E7-18C7-4672-ABE8-29596E7E9B72}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlowCheck.BL", "WorkFlowCheck.BL\WorkFlowCheck.BL.csproj", "{73049F81-7A5D-4819-ADB8-A9926672365E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlowCheck.Common", "WorkFlowCheck.Common\WorkFlowCheck.Common.csproj", "{7F94D6CB-3358-4687-88E1-FA46053D6B71}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,12 +27,6 @@ Global {6B46795C-DF5A-4F12-95A9-691BC1741AB8}.Debug|Any CPU.Build.0 = Debug|Any CPU {6B46795C-DF5A-4F12-95A9-691BC1741AB8}.Release|Any CPU.ActiveCfg = Release|Any CPU {6B46795C-DF5A-4F12-95A9-691BC1741AB8}.Release|Any CPU.Build.0 = Release|Any CPU - {0E2BD120-EE91-4A24-A78A-CE7CA87E09AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0E2BD120-EE91-4A24-A78A-CE7CA87E09AE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0E2BD120-EE91-4A24-A78A-CE7CA87E09AE}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {0E2BD120-EE91-4A24-A78A-CE7CA87E09AE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0E2BD120-EE91-4A24-A78A-CE7CA87E09AE}.Release|Any CPU.Build.0 = Release|Any CPU - {0E2BD120-EE91-4A24-A78A-CE7CA87E09AE}.Release|Any CPU.Deploy.0 = Release|Any CPU {25A8C0E7-18C7-4672-ABE8-29596E7E9B72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {25A8C0E7-18C7-4672-ABE8-29596E7E9B72}.Debug|Any CPU.Build.0 = Debug|Any CPU {25A8C0E7-18C7-4672-ABE8-29596E7E9B72}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -41,6 +35,10 @@ Global {73049F81-7A5D-4819-ADB8-A9926672365E}.Debug|Any CPU.Build.0 = Debug|Any CPU {73049F81-7A5D-4819-ADB8-A9926672365E}.Release|Any CPU.ActiveCfg = Release|Any CPU {73049F81-7A5D-4819-ADB8-A9926672365E}.Release|Any CPU.Build.0 = Release|Any CPU + {7F94D6CB-3358-4687-88E1-FA46053D6B71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F94D6CB-3358-4687-88E1-FA46053D6B71}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F94D6CB-3358-4687-88E1-FA46053D6B71}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F94D6CB-3358-4687-88E1-FA46053D6B71}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/WorkFlowCheck.API/Controllers/UserController.cs b/src/WorkFlowCheck.API/Controllers/UserController.cs index bffb1a5..7d998f7 100644 --- a/src/WorkFlowCheck.API/Controllers/UserController.cs +++ b/src/WorkFlowCheck.API/Controllers/UserController.cs @@ -1,7 +1,8 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using WorkFlowCheck.BL.DTO; +using WorkFlowCheck.Common.DTO; using WorkFlowCheck.BL.Services.Interfaces; +using Serilog; namespace WorkFlowCheck.API.Controllers { @@ -47,5 +48,53 @@ namespace WorkFlowCheck.API.Controllers return retVal; } + + [HttpPost("authenticate")] + public async Task> Authenticate([FromBody] UserDTO userDTO) + { + var retVal = new ApiResponseDTO() + { + IsSuccess = true + }; + if (string.IsNullOrEmpty(userDTO.UserName) || string.IsNullOrEmpty(userDTO.Password)) + { + retVal.IsSuccess = false; + retVal.Errors.Add("Nem megfelelő felhasználói név vagy jelszó"); + return retVal; + } + try + { + var userDTO_Response = await _userService.Authenticate(userDTO.UserName, userDTO.Password); + if (userDTO_Response != null) + { + if (string.IsNullOrEmpty(userDTO_Response.UserName) == false && userDTO_Response.UserName == userDTO.UserName) + { + retVal.IsSuccess = true; + retVal.Data = userDTO_Response; + Log.Information($"Sikeres azonosítás! {userDTO.UserName}"); + } + else + { + var error = $"Nem megfelelő felhasználó vagy jelszó! {userDTO.UserName}"; + retVal.IsSuccess = false; + retVal.Errors.Add(error); + Log.Information(error); + } + } + else + { + var error = $"Nem megfelelő felhasználó vagy jelszó! {userDTO.UserName}"; + retVal.IsSuccess = false; + retVal.Errors.Add(error); + Log.Information(error); + } + } + catch (Exception ex) + { + + Log.Error(ex.Message); + } + return retVal; + } } } diff --git a/src/WorkFlowCheck.API/Program.cs b/src/WorkFlowCheck.API/Program.cs index a9439af..1a337a2 100644 --- a/src/WorkFlowCheck.API/Program.cs +++ b/src/WorkFlowCheck.API/Program.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Mvc; using Serilog; using WorkFlowCheck.BL.Mappings; using WorkFlowCheck.BL.Infra; +using WorkFlowCheck.DL.Seed; var builder = WebApplication.CreateBuilder(args); @@ -77,6 +78,12 @@ builder.Services.AddControllers() var app = builder.Build(); +using (var scope = app.Services.CreateScope()) +{ + var context = scope.ServiceProvider.GetRequiredService(); + DBInitializer.Seed(context); +} + // Swagger middleware if (app.Environment.IsDevelopment()) { diff --git a/src/WorkFlowCheck.BL/Mappings/MapperProfile.cs b/src/WorkFlowCheck.BL/Mappings/MapperProfile.cs index 30398b3..b8dca27 100644 --- a/src/WorkFlowCheck.BL/Mappings/MapperProfile.cs +++ b/src/WorkFlowCheck.BL/Mappings/MapperProfile.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; using AutoMapper; using AutoMapper.Configuration.Conventions; -using WorkFlowCheck.BL.DTO; +using WorkFlowCheck.Common.DTO; using WorkFlowCheck.DL.Entities; namespace WorkFlowCheck.BL.Mappings @@ -15,6 +15,9 @@ namespace WorkFlowCheck.BL.Mappings public MapperProfile() { CreateMap(); + CreateMap() + .ForMember(dest => dest.PasswordHash, opt => opt.MapFrom()); + } } } diff --git a/src/WorkFlowCheck.BL/Mappings/PasswordHashResolver.cs b/src/WorkFlowCheck.BL/Mappings/PasswordHashResolver.cs new file mode 100644 index 0000000..23367d7 --- /dev/null +++ b/src/WorkFlowCheck.BL/Mappings/PasswordHashResolver.cs @@ -0,0 +1,20 @@ +using AutoMapper; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WorkFlowCheck.Common.DTO; +using WorkFlowCheck.Common.Security; +using WorkFlowCheck.DL.Entities; + +namespace WorkFlowCheck.BL.Mappings +{ + public class PasswordHashResolver : IValueResolver + { + public string Resolve(UserDTO source, User destination, string destMember, ResolutionContext context) + { + return PasswordHasher.HashPassword(source.Password); + } + } +} diff --git a/src/WorkFlowCheck.BL/Services/Interfaces/IUserService.cs b/src/WorkFlowCheck.BL/Services/Interfaces/IUserService.cs index 28496a9..59945d3 100644 --- a/src/WorkFlowCheck.BL/Services/Interfaces/IUserService.cs +++ b/src/WorkFlowCheck.BL/Services/Interfaces/IUserService.cs @@ -3,12 +3,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using WorkFlowCheck.BL.DTO; +using WorkFlowCheck.Common.DTO; namespace WorkFlowCheck.BL.Services.Interfaces { public interface IUserService { Task GetUser(int Id); + Task Authenticate(string UserName, string Password); } } diff --git a/src/WorkFlowCheck.BL/Services/UserService.cs b/src/WorkFlowCheck.BL/Services/UserService.cs index 517d842..0fdb2ae 100644 --- a/src/WorkFlowCheck.BL/Services/UserService.cs +++ b/src/WorkFlowCheck.BL/Services/UserService.cs @@ -6,9 +6,10 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using WorkFlowCheck.BL.DTO; +using WorkFlowCheck.Common.DTO; using WorkFlowCheck.BL.Services.Interfaces; using WorkFlowCheck.DL; +using WorkFlowCheck.Common.Security; namespace WorkFlowCheck.BL.Services { @@ -39,5 +40,24 @@ namespace WorkFlowCheck.BL.Services } return retVal; } + + public async Task Authenticate(string userName, string password) + { + var retVal = new UserDTO(); + + try + { + var user = await _dbContext.Users.Where(w => w.UserName == userName).FirstOrDefaultAsync(); + if (user != null && PasswordHasher.VerifyPassword(user.PasswordHash, password)) + { + retVal = _mapper.Map(user); + } + } + catch (Exception ex) + { + Log.Error(ex.Message); + } + return retVal; + } } } diff --git a/src/WorkFlowCheck.BL/WorkFlowCheck.BL.csproj b/src/WorkFlowCheck.BL/WorkFlowCheck.BL.csproj index 1faa4c4..b5c6763 100644 --- a/src/WorkFlowCheck.BL/WorkFlowCheck.BL.csproj +++ b/src/WorkFlowCheck.BL/WorkFlowCheck.BL.csproj @@ -13,6 +13,7 @@ + diff --git a/src/WorkFlowCheck.Common/DTO/UserDTO.cs b/src/WorkFlowCheck.Common/DTO/UserDTO.cs index d36daa7..225104a 100644 --- a/src/WorkFlowCheck.Common/DTO/UserDTO.cs +++ b/src/WorkFlowCheck.Common/DTO/UserDTO.cs @@ -6,5 +6,7 @@ public string Email { get; set; } = null!; public string FirstName { get; set; } = null!; public string LastName { get; set; } = null!; + public string UserName { get;set; } = null!; + public string Password { get; set; } } } diff --git a/src/WorkFlowCheck.Common/Security/PasswordHasher.cs b/src/WorkFlowCheck.Common/Security/PasswordHasher.cs new file mode 100644 index 0000000..69d524a --- /dev/null +++ b/src/WorkFlowCheck.Common/Security/PasswordHasher.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace WorkFlowCheck.Common.Security +{ + public static class PasswordHasher + { + // Jelszó hashelése + public static string HashPassword(string password) + { + // Generate a random salt + byte[] salt = new byte[16]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(salt); + } + + // Hash the password with the salt + using (var rfc2898 = new Rfc2898DeriveBytes(password, salt, 10000, HashAlgorithmName.SHA256)) + { + byte[] hash = rfc2898.GetBytes(32); // 256-bit hash + byte[] hashBytes = new byte[48]; // Salt (16 bytes) + Hash (32 bytes) + Array.Copy(salt, 0, hashBytes, 0, 16); + Array.Copy(hash, 0, hashBytes, 16, 32); + + return Convert.ToBase64String(hashBytes); + } + } + + // Jelszó ellenőrzése + public static bool VerifyPassword(string storedPasswordHash, string inputPassword) + { + byte[] hashBytes = Convert.FromBase64String(storedPasswordHash); + + // Extract salt (first 16 bytes) and stored hash (next 32 bytes) + byte[] salt = new byte[16]; + byte[] storedHash = new byte[32]; + Array.Copy(hashBytes, 0, salt, 0, 16); + Array.Copy(hashBytes, 16, storedHash, 0, 32); + + // Hash the input password with the same salt + using (var rfc2898 = new Rfc2898DeriveBytes(inputPassword, salt, 10000, HashAlgorithmName.SHA256)) + { + byte[] inputHash = rfc2898.GetBytes(32); + + // Compare the stored hash and the calculated hash + return CryptographicOperations.FixedTimeEquals(storedHash, inputHash); + } + } + } +} diff --git a/src/WorkFlowCheck.DL/Entities/User.cs b/src/WorkFlowCheck.DL/Entities/User.cs index 84e7853..0bc318f 100644 --- a/src/WorkFlowCheck.DL/Entities/User.cs +++ b/src/WorkFlowCheck.DL/Entities/User.cs @@ -8,5 +8,7 @@ namespace WorkFlowCheck.DL.Entities public string Email { get; set; } = null!; public string FirstName { get; set; } = null!; public string LastName { get; set; } = null!; + public string UserName { get; set; } = null!; + public string PasswordHash { get; set; } } } diff --git a/src/WorkFlowCheck.DL/Seed/DBInitializer.cs b/src/WorkFlowCheck.DL/Seed/DBInitializer.cs new file mode 100644 index 0000000..94bff3c --- /dev/null +++ b/src/WorkFlowCheck.DL/Seed/DBInitializer.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WorkFlowCheck.DL.Seed +{ + public static class DBInitializer + { + public static void Seed(AppDbContext dbContext) + { + + } + } +}