diff --git a/Controllers/PdfController.cs b/Controllers/PdfController.cs index f054a4a..c823329 100644 --- a/Controllers/PdfController.cs +++ b/Controllers/PdfController.cs @@ -1,4 +1,21 @@ -using System; +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ + +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/Controllers/UserController.cs b/Controllers/UserController.cs index bc4ef8b..c61879f 100644 --- a/Controllers/UserController.cs +++ b/Controllers/UserController.cs @@ -1,4 +1,20 @@ -using FirmTracker_Server.Models; +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +using FirmTracker_Server.Models; using FirmTracker_Server.Services; using FirmTracker_Server; using Microsoft.AspNetCore.Authorization; @@ -6,11 +22,14 @@ using Microsoft.AspNetCore.Mvc; using FirmTracker_Server.Entities; using System.Security.Claims; +using System.Security.Cryptography; +using System.Text; + namespace FirmTracker_Server.Controllers { [Route("api/user")] [ApiController] - [Authorize] + //[Authorize] public class UserController : ControllerBase { private readonly IUserService UserService; @@ -21,7 +40,7 @@ namespace FirmTracker_Server.Controllers } [HttpPost("create")] - [Authorize(Roles = Roles.Admin)] + //[Authorize(Roles = Roles.Admin)] public ActionResult CreateUser([FromBody] CreateUserDto dto) { if (!ModelState.IsValid) @@ -62,6 +81,51 @@ namespace FirmTracker_Server.Controllers return Ok(emails); } + [HttpPost("ChangeUserPassword")] + [Authorize(Roles = Roles.Admin)] + public ActionResult ChangeUserPassword([FromBody] ChangeUserPasswordDto dto) + { + try + { + var result = UserService.ChangeUserPassword(dto); + if (result) + { + return Ok("Password changed successfully."); + } + else + { + return BadRequest("Failed to change the password."); + } + } + catch (Exception ex) + { + return BadRequest($"An error occurred: {ex.Message}"); + } + } + [HttpPost("changePassword")] + [Authorize(Roles = Roles.Admin + "," + Roles.User)] + public ActionResult ChangePassword([FromBody] UpdatePasswordDto dto) + { + try + { + var result = UserService.UpdatePassword(dto); + if (result) + { + var loginDto = new LoginDto { Email = dto.email, Password = dto.newPassword }; + var token = UserService.CreateTokenJwt(loginDto); + return Ok(new { Token = token }); + } + else + { + return BadRequest("Failed to change the password."); + } + } + catch (Exception ex) + { + return BadRequest($"An error occurred: {ex.Message}"); + } + } + // New method to get all users /* [HttpGet("all")] [AllowAnonymous] diff --git a/Controllers/WorkDayController.cs b/Controllers/WorkDayController.cs index 9ed9a42..737d74d 100644 --- a/Controllers/WorkDayController.cs +++ b/Controllers/WorkDayController.cs @@ -76,8 +76,23 @@ namespace FirmTracker_Server.Controllers } } + [HttpGet("user/workdays")] + [Authorize(Roles = Roles.Admin + "," + Roles.User)] + public IActionResult GetWorkdaysLoggedUser() + { + try + { + var userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; + + var workdays = _workdayCRUD.GetWorkdaysByLoggedUser(userId); + return Ok(workdays); + } + catch (Exception ex) + { + return BadRequest(new { message = "An error occurred while fetching workdays.", error = ex.Message }); + } + } - // Endpoint to get all workdays for a user [HttpGet("user/{userMail}/workdays")] [Authorize(Roles = Roles.Admin + "," + Roles.User)] diff --git a/FirmTracker-Server.csproj b/FirmTracker-Server.csproj index 3ec9a97..c4c466e 100644 --- a/FirmTracker-Server.csproj +++ b/FirmTracker-Server.csproj @@ -35,12 +35,6 @@ - - - ./szyfrowanie.dll - - - True diff --git a/Models/AddAbsenceDtocs.cs b/Models/AddAbsenceDtocs.cs index ba80d3d..0a00b16 100644 --- a/Models/AddAbsenceDtocs.cs +++ b/Models/AddAbsenceDtocs.cs @@ -1,4 +1,20 @@ -namespace FirmTracker_Server.Models +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +namespace FirmTracker_Server.Models { public class AddAbsenceDto { diff --git a/Models/ChangeUserPasswordDto.cs b/Models/ChangeUserPasswordDto.cs new file mode 100644 index 0000000..b465eec --- /dev/null +++ b/Models/ChangeUserPasswordDto.cs @@ -0,0 +1,24 @@ +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +namespace FirmTracker_Server.Models +{ + public class ChangeUserPasswordDto + { + public string email { get; set; } + public string password { get; set; } + } +} diff --git a/Models/CreateUserDto.cs b/Models/CreateUserDto.cs index 41895b9..68bb2be 100644 --- a/Models/CreateUserDto.cs +++ b/Models/CreateUserDto.cs @@ -1,4 +1,20 @@ -namespace FirmTracker_Server.Models +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +namespace FirmTracker_Server.Models { public class CreateUserDto { diff --git a/Models/DayDetailsDto.cs b/Models/DayDetailsDto.cs index bad9080..11aaabf 100644 --- a/Models/DayDetailsDto.cs +++ b/Models/DayDetailsDto.cs @@ -1,4 +1,20 @@ -using FirmTracker_Server.nHibernate; +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +using FirmTracker_Server.nHibernate; namespace FirmTracker_Server.Models { diff --git a/Models/DayDetailsLoggedUserDto.cs b/Models/DayDetailsLoggedUserDto.cs index 77f3293..45b5f53 100644 --- a/Models/DayDetailsLoggedUserDto.cs +++ b/Models/DayDetailsLoggedUserDto.cs @@ -1,4 +1,20 @@ -using FirmTracker_Server.nHibernate; +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +using FirmTracker_Server.nHibernate; namespace FirmTracker_Server.Models { diff --git a/Models/EmployeeDto.cs b/Models/EmployeeDto.cs index 7a30e5b..08a7e31 100644 --- a/Models/EmployeeDto.cs +++ b/Models/EmployeeDto.cs @@ -1,4 +1,20 @@ -using FirmTracker_Server.Controllers; +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +using FirmTracker_Server.Controllers; namespace FirmTracker_Server.Models { diff --git a/Models/LoginDtocs.cs b/Models/LoginDtocs.cs index 65628d7..0473da4 100644 --- a/Models/LoginDtocs.cs +++ b/Models/LoginDtocs.cs @@ -1,4 +1,20 @@ -namespace FirmTracker_Server.Models +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +namespace FirmTracker_Server.Models { public class LoginDto { diff --git a/Models/UpdateAbsenceDto.cs b/Models/UpdateAbsenceDto.cs index db32c06..4d65372 100644 --- a/Models/UpdateAbsenceDto.cs +++ b/Models/UpdateAbsenceDto.cs @@ -1,4 +1,20 @@ -namespace FirmTracker_Server.Models +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +namespace FirmTracker_Server.Models { public class UpdateAbsenceDto { diff --git a/Models/UpdatePasswordDto.cs b/Models/UpdatePasswordDto.cs index 251b9dd..9a36f0a 100644 --- a/Models/UpdatePasswordDto.cs +++ b/Models/UpdatePasswordDto.cs @@ -1,8 +1,27 @@ -namespace FirmTracker_Server.Models +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +namespace FirmTracker_Server.Models { public class UpdatePasswordDto { - public string Email { get; set; } - public string Password { get; set; } + public string email { get; set; } + public string oldPassword { get; set; } + public string newPassword { get; set; } + + } } diff --git a/Models/UserDto.cs b/Models/UserDto.cs index da941b6..388a624 100644 --- a/Models/UserDto.cs +++ b/Models/UserDto.cs @@ -1,4 +1,20 @@ -using System.ComponentModel.DataAnnotations; +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +using System.ComponentModel.DataAnnotations; namespace FirmTracker_Server.Models { diff --git a/Models/Workday.cs b/Models/Workday.cs index 31313b8..5f0126c 100644 --- a/Models/Workday.cs +++ b/Models/Workday.cs @@ -1,4 +1,20 @@ -using FirmTracker_Server.Entities; +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ +using FirmTracker_Server.Entities; using System; namespace YourNamespace.Models diff --git a/Services/UserService.cs b/Services/UserService.cs index 8e9d3b9..04cc88f 100644 --- a/Services/UserService.cs +++ b/Services/UserService.cs @@ -3,15 +3,12 @@ using FirmTracker_Server.Authentication; using FirmTracker_Server.Entities; using FirmTracker_Server.Exceptions; using FirmTracker_Server.Models; -using FirmTracker_Server.Authentication; -using FirmTracker_Server.Exceptions; using Microsoft.AspNetCore.Identity; using Microsoft.IdentityModel.Tokens; using System.Globalization; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; -using szyfrowanie; using FirmTracker_Server.nHibernate; using NHibernate; using NHibernate.Criterion; @@ -27,29 +24,84 @@ namespace FirmTracker_Server.Services string CreateTokenJwt(LoginDto dto); IEnumerable GetAllUserEmails(); bool UpdatePassword(UpdatePasswordDto dto); + bool ChangeUserPassword(ChangeUserPasswordDto dto); } public class UserService : IUserService { - // private readonly GeneralDbContext DbContext; + // private readonly GeneralDbContext DbContext; private readonly IMapper Mapper; private readonly IPasswordHasher PasswordHasher; private readonly AuthenticationSettings AuthenticationSettings; - private readonly SimplerAES SimplerAES; + // private readonly SimplerAES SimplerAES; //private readonly SessionFactory sessionFactory; - public UserService( IMapper mapper, IPasswordHasher passwordHasher, AuthenticationSettings authenticationSettings) + public UserService(IMapper mapper, IPasswordHasher passwordHasher, AuthenticationSettings authenticationSettings) { - // DbContext = dbContext; + // DbContext = dbContext; Mapper = mapper; PasswordHasher = passwordHasher; AuthenticationSettings = authenticationSettings; - SimplerAES = new SimplerAES(); + ///SimplerAES = new SimplerAES(); //SessionFactory = sessionFactory; } + public bool ChangeUserPassword(ChangeUserPasswordDto dto) + { + using (var session = SessionFactory.OpenSession()) + using (var transaction = session.BeginTransaction()) + { + try + { + var user = session.Query().FirstOrDefault(u => u.Email == dto.email); + if (user == null) + { + throw new Exception("User not found."); + } + + user.PassHash = PasswordHasher.HashPassword(user, dto.password); + session.Update(user); + transaction.Commit(); + + return true; + } + catch + { + transaction.Rollback(); + throw; + } + } + } public bool UpdatePassword(UpdatePasswordDto dto) { - return true; + using (var session = SessionFactory.OpenSession()) + using (var transaction = session.BeginTransaction()) + { + try + { + var user = session.Query().FirstOrDefault(u => u.Email == dto.email); + if (user == null) + { + throw new Exception("User not found."); + } + + var result = PasswordHasher.VerifyHashedPassword(user, user.PassHash, dto.oldPassword); + if (result != PasswordVerificationResult.Success) + { + throw new Exception("Invalid current password."); + } + + user.PassHash = PasswordHasher.HashPassword(user, dto.newPassword); + session.Update(user); + transaction.Commit(); + + return true; + } + catch + { + transaction.Rollback(); + throw; + } + } } public IEnumerable GetAllUserEmails() { @@ -74,7 +126,7 @@ namespace FirmTracker_Server.Services var user = Mapper.Map(dto); // Encrypt or hash the password based on NewEncryption flag - user.PassHash = dto.NewEncryption ? SimplerAES.Encrypt(dto.Password) : PasswordHasher.HashPassword(user, dto.Password); + user.PassHash = dto.NewEncryption ? PasswordHasher.HashPassword(user, dto.Password) : PasswordHasher.HashPassword(user, dto.Password); user.Role = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(dto.Role.ToLower()); using (var session = SessionFactory.OpenSession()) @@ -119,9 +171,9 @@ namespace FirmTracker_Server.Services { try { - Console.WriteLine(SimplerAES.Decrypt(user.PassHash)+" "+SimplerAES.Decrypt(dto.Password)); - var ready = SimplerAES.Decrypt(user.PassHash) == SimplerAES.Decrypt(dto.Password); - if (!ready) + Console.WriteLine(PasswordHasher.HashPassword(user, user.PassHash)); + var ready = PasswordHasher.VerifyHashedPassword(user, user.PassHash, dto.Password); + if (ready == 0) { throw new WrongUserOrPasswordException("Nieprawidłowy login lub hasło."); } @@ -134,7 +186,7 @@ namespace FirmTracker_Server.Services else { var ready = PasswordVerificationResult.Failed; - if (SimplerAES.Decrypt(user.PassHash) == SimplerAES.Decrypt(dto.Password)) { ready = PasswordVerificationResult.Success; } //PasswordHasher.VerifyHashedPassword(user, user.PassHash, dto.Password); + if (PasswordHasher.VerifyHashedPassword(user, user.PassHash, dto.Password) == PasswordVerificationResult.Success) { ready = PasswordVerificationResult.Success; } //PasswordHasher.VerifyHashedPassword(user, user.PassHash, dto.Password); if (ready == PasswordVerificationResult.Failed) { throw new WrongUserOrPasswordException("Nieprawidłowy login lub hasło."); diff --git a/TestClass.cs b/TestClass.cs index 761d907..fbe9a25 100644 --- a/TestClass.cs +++ b/TestClass.cs @@ -186,8 +186,8 @@ namespace FirmTracker_Server //SessionFactory.Init(connectionString); - string queryUser = "insert into Users(Email,PassHash,Role) select '123@wp.pl', 'GOsGemJarMJu8btZKF6Rung27JLZkdO7Wfd4CwLhL1k=','User'"; - string queryAdmin = "insert into Users(Email,PassHash,Role) select '321@wp.pl', 'GOsGemJarMJu8btZKF6Rung27JLZkdO7Wfd4CwLhL1k=','Admin'"; + string queryUser = "insert into Users(Email,PassHash,Role) select '123@wp.pl', 'AQAAAAIAAYagAAAAEMQUuFPUNAddMmuZpCUAZpaDR31+BqMJhnamIAllDi+aTBJQ7tEtLuEMppgz0oLYyw==','User'"; + string queryAdmin = "insert into Users(Email,PassHash,Role) select '321@wp.pl', 'AQAAAAIAAYagAAAAEMQUuFPUNAddMmuZpCUAZpaDR31+BqMJhnamIAllDi+aTBJQ7tEtLuEMppgz0oLYyw==','Admin'"; SqlConnection connection = new SqlConnection(connectionString); @@ -237,72 +237,7 @@ namespace FirmTracker_Server expenseCrud.AddExpense(expense1); expenseCrud.AddExpense(expense2); expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); - expenseCrud.AddExpense(expense3); + List testTransactionProducts = new List {