This commit is contained in:
Maciej Maciejewski 2025-01-02 22:48:51 +01:00
parent b9dd32ee4d
commit 30378d95ab
17 changed files with 369 additions and 105 deletions

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
using FirmTracker_Server.Models;
using FirmTracker_Server.Services; using FirmTracker_Server.Services;
using FirmTracker_Server; using FirmTracker_Server;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@ -6,11 +22,14 @@ using Microsoft.AspNetCore.Mvc;
using FirmTracker_Server.Entities; using FirmTracker_Server.Entities;
using System.Security.Claims; using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
namespace FirmTracker_Server.Controllers namespace FirmTracker_Server.Controllers
{ {
[Route("api/user")] [Route("api/user")]
[ApiController] [ApiController]
[Authorize] //[Authorize]
public class UserController : ControllerBase public class UserController : ControllerBase
{ {
private readonly IUserService UserService; private readonly IUserService UserService;
@ -21,7 +40,7 @@ namespace FirmTracker_Server.Controllers
} }
[HttpPost("create")] [HttpPost("create")]
[Authorize(Roles = Roles.Admin)] //[Authorize(Roles = Roles.Admin)]
public ActionResult CreateUser([FromBody] CreateUserDto dto) public ActionResult CreateUser([FromBody] CreateUserDto dto)
{ {
if (!ModelState.IsValid) if (!ModelState.IsValid)
@ -62,6 +81,51 @@ namespace FirmTracker_Server.Controllers
return Ok(emails); 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 // New method to get all users
/* [HttpGet("all")] /* [HttpGet("all")]
[AllowAnonymous] [AllowAnonymous]

View File

@ -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 // Endpoint to get all workdays for a user
[HttpGet("user/{userMail}/workdays")] [HttpGet("user/{userMail}/workdays")]
[Authorize(Roles = Roles.Admin + "," + Roles.User)] [Authorize(Roles = Roles.Admin + "," + Roles.User)]

View File

@ -35,12 +35,6 @@
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.1.2" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.1.2" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="szyfrowanie">
<HintPath>./szyfrowanie.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Properties\Resources.Designer.cs"> <Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
namespace FirmTracker_Server.Models
{ {
public class AddAbsenceDto public class AddAbsenceDto
{ {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
namespace FirmTracker_Server.Models
{
public class ChangeUserPasswordDto
{
public string email { get; set; }
public string password { get; set; }
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
namespace FirmTracker_Server.Models
{ {
public class CreateUserDto public class CreateUserDto
{ {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
using FirmTracker_Server.nHibernate;
namespace FirmTracker_Server.Models namespace FirmTracker_Server.Models
{ {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
using FirmTracker_Server.nHibernate;
namespace FirmTracker_Server.Models namespace FirmTracker_Server.Models
{ {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
using FirmTracker_Server.Controllers;
namespace FirmTracker_Server.Models namespace FirmTracker_Server.Models
{ {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
namespace FirmTracker_Server.Models
{ {
public class LoginDto public class LoginDto
{ {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
namespace FirmTracker_Server.Models
{ {
public class UpdateAbsenceDto public class UpdateAbsenceDto
{ {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
namespace FirmTracker_Server.Models
{ {
public class UpdatePasswordDto public class UpdatePasswordDto
{ {
public string Email { get; set; } public string email { get; set; }
public string Password { get; set; } public string oldPassword { get; set; }
public string newPassword { get; set; }
} }
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
using System.ComponentModel.DataAnnotations;
namespace FirmTracker_Server.Models namespace FirmTracker_Server.Models
{ {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
using FirmTracker_Server.Entities;
using System; using System;
namespace YourNamespace.Models namespace YourNamespace.Models

View File

@ -3,15 +3,12 @@ using FirmTracker_Server.Authentication;
using FirmTracker_Server.Entities; using FirmTracker_Server.Entities;
using FirmTracker_Server.Exceptions; using FirmTracker_Server.Exceptions;
using FirmTracker_Server.Models; using FirmTracker_Server.Models;
using FirmTracker_Server.Authentication;
using FirmTracker_Server.Exceptions;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.Globalization; using System.Globalization;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using szyfrowanie;
using FirmTracker_Server.nHibernate; using FirmTracker_Server.nHibernate;
using NHibernate; using NHibernate;
using NHibernate.Criterion; using NHibernate.Criterion;
@ -27,29 +24,84 @@ namespace FirmTracker_Server.Services
string CreateTokenJwt(LoginDto dto); string CreateTokenJwt(LoginDto dto);
IEnumerable<string> GetAllUserEmails(); IEnumerable<string> GetAllUserEmails();
bool UpdatePassword(UpdatePasswordDto dto); bool UpdatePassword(UpdatePasswordDto dto);
bool ChangeUserPassword(ChangeUserPasswordDto dto);
} }
public class UserService : IUserService public class UserService : IUserService
{ {
// private readonly GeneralDbContext DbContext; // private readonly GeneralDbContext DbContext;
private readonly IMapper Mapper; private readonly IMapper Mapper;
private readonly IPasswordHasher<User> PasswordHasher; private readonly IPasswordHasher<User> PasswordHasher;
private readonly AuthenticationSettings AuthenticationSettings; private readonly AuthenticationSettings AuthenticationSettings;
private readonly SimplerAES SimplerAES; // private readonly SimplerAES SimplerAES;
//private readonly SessionFactory sessionFactory; //private readonly SessionFactory sessionFactory;
public UserService( IMapper mapper, IPasswordHasher<User> passwordHasher, AuthenticationSettings authenticationSettings) public UserService(IMapper mapper, IPasswordHasher<User> passwordHasher, AuthenticationSettings authenticationSettings)
{ {
// DbContext = dbContext; // DbContext = dbContext;
Mapper = mapper; Mapper = mapper;
PasswordHasher = passwordHasher; PasswordHasher = passwordHasher;
AuthenticationSettings = authenticationSettings; AuthenticationSettings = authenticationSettings;
SimplerAES = new SimplerAES(); ///SimplerAES = new SimplerAES();
//SessionFactory = sessionFactory; //SessionFactory = sessionFactory;
} }
public bool ChangeUserPassword(ChangeUserPasswordDto dto)
{
using (var session = SessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
try
{
var user = session.Query<User>().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) public bool UpdatePassword(UpdatePasswordDto dto)
{ {
return true; using (var session = SessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
try
{
var user = session.Query<User>().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<string> GetAllUserEmails() public IEnumerable<string> GetAllUserEmails()
{ {
@ -74,7 +126,7 @@ namespace FirmTracker_Server.Services
var user = Mapper.Map<User>(dto); var user = Mapper.Map<User>(dto);
// Encrypt or hash the password based on NewEncryption flag // 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()); user.Role = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(dto.Role.ToLower());
using (var session = SessionFactory.OpenSession()) using (var session = SessionFactory.OpenSession())
@ -119,9 +171,9 @@ namespace FirmTracker_Server.Services
{ {
try try
{ {
Console.WriteLine(SimplerAES.Decrypt(user.PassHash)+" "+SimplerAES.Decrypt(dto.Password)); Console.WriteLine(PasswordHasher.HashPassword(user, user.PassHash));
var ready = SimplerAES.Decrypt(user.PassHash) == SimplerAES.Decrypt(dto.Password); var ready = PasswordHasher.VerifyHashedPassword(user, user.PassHash, dto.Password);
if (!ready) if (ready == 0)
{ {
throw new WrongUserOrPasswordException("Nieprawidłowy login lub hasło."); throw new WrongUserOrPasswordException("Nieprawidłowy login lub hasło.");
} }
@ -134,7 +186,7 @@ namespace FirmTracker_Server.Services
else else
{ {
var ready = PasswordVerificationResult.Failed; 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) if (ready == PasswordVerificationResult.Failed)
{ {
throw new WrongUserOrPasswordException("Nieprawidłowy login lub hasło."); throw new WrongUserOrPasswordException("Nieprawidłowy login lub hasło.");

View File

@ -186,8 +186,8 @@ namespace FirmTracker_Server
//SessionFactory.Init(connectionString); //SessionFactory.Init(connectionString);
string queryUser = "insert into Users(Email,PassHash,Role) select '123@wp.pl', 'GOsGemJarMJu8btZKF6Rung27JLZkdO7Wfd4CwLhL1k=','User'"; 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', 'GOsGemJarMJu8btZKF6Rung27JLZkdO7Wfd4CwLhL1k=','Admin'"; string queryAdmin = "insert into Users(Email,PassHash,Role) select '321@wp.pl', 'AQAAAAIAAYagAAAAEMQUuFPUNAddMmuZpCUAZpaDR31+BqMJhnamIAllDi+aTBJQ7tEtLuEMppgz0oLYyw==','Admin'";
SqlConnection connection = new SqlConnection(connectionString); SqlConnection connection = new SqlConnection(connectionString);
@ -237,72 +237,7 @@ namespace FirmTracker_Server
expenseCrud.AddExpense(expense1); expenseCrud.AddExpense(expense1);
expenseCrud.AddExpense(expense2); 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);
expenseCrud.AddExpense(expense3);
List<TransactionProduct> testTransactionProducts = new List<TransactionProduct> { List<TransactionProduct> testTransactionProducts = new List<TransactionProduct> {