using System.Collections; using BasicDotnetTemplate.MainProject.Core.Database; using BasicDotnetTemplate.MainProject.Models.Api.Common.Exceptions; using BasicDotnetTemplate.MainProject.Models.Api.Data.User; using BasicDotnetTemplate.MainProject.Models.Database.SqlServer; using BasicDotnetTemplate.MainProject.Utils; using Microsoft.EntityFrameworkCore; namespace BasicDotnetTemplate.MainProject.Services; public interface IUserService { Task GetUserByIdAsync(int id); Task GetUserByGuidAsync(string guid); Task GetUserByUsernameAndPassword(string email, string password); Task CheckIfEmailIsValid(string email, string? guid = ""); Task CreateUserAsync(CreateUserRequestData data, Role role); Task DeleteUserAsync(User user); } public class UserService : BaseService, IUserService { private readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); public UserService( IHttpContextAccessor httpContextAccessor, IConfiguration configuration, SqlServerContext sqlServerContext ) : base(httpContextAccessor, configuration, sqlServerContext) { } private IQueryable GetUsersQueryable() { return this._sqlServerContext.Users.Where(x => !x.IsDeleted); } private IQueryable GetUserByEmailQueryable(string email) { return this.GetUsersQueryable().Where(x => x.Email.ToString() == email.ToString() ); } private User CreateUserData(CreateUserRequestData data, Role role) { var salt = _appSettings.EncryptionSettings?.Salt ?? String.Empty; var pepper = CryptUtils.GeneratePepper(); var iterations = _appSettings.EncryptionSettings?.Iterations ?? 10; User user = new() { CreationTime = DateTime.UtcNow, CreationUserId = this.GetCurrentUserId(), IsDeleted = false, Guid = Guid.NewGuid().ToString(), FirstName = data.FirstName, LastName = data.LastName, Email = data.Email, PasswordSalt = salt, PasswordPepper = pepper, PasswordIterations = iterations, Password = CryptUtils.GeneratePassword(data.Password, salt, iterations, pepper), Role = role, IsTestUser = false }; return user; } public async Task GetUserByIdAsync(int id) { return await this.GetUsersQueryable().Where(x => x.Id == id).FirstOrDefaultAsync(); } public async Task GetUserByGuidAsync(string guid) { return await this.GetUsersQueryable().Where(x => x.Guid == guid).FirstOrDefaultAsync(); } public async Task GetUserByUsernameAndPassword(string email, string password) { User? user = await this.GetUserByEmailQueryable(email).FirstOrDefaultAsync(); if (user != null) { var valid = CryptUtils.VerifyPassword(user.Password, password, user.PasswordSalt, user.PasswordIterations, user.PasswordPepper); if (!valid) user = null; } return user; } public async Task CheckIfEmailIsValid(string email, string? guid = "") { var valid = false; User? user = await this.GetUserByEmailQueryable(email).FirstOrDefaultAsync(); if (user != null) { if (!String.IsNullOrEmpty(guid)) { valid = user.Guid == guid && user.Email == email; } } else { valid = true; } return valid; } public async Task CreateUserAsync(CreateUserRequestData data, Role role) { User? user; using var transaction = await _sqlServerContext.Database.BeginTransactionAsync(); try { var tempUser = CreateUserData(data, role); await _sqlServerContext.Users.AddAsync(tempUser); await _sqlServerContext.SaveChangesAsync(); await transaction.CommitAsync(); user = tempUser; } catch (Exception exception) { await transaction.RollbackAsync(); Logger.Error(exception, $"[UserService][CreateUserAsync]"); throw new CreateException($"An error occurred while creating the user for transaction ID {transaction.TransactionId}.", exception); } return user; } public async Task DeleteUserAsync(User user) { bool? deleted = false; using (var transaction = _sqlServerContext.Database.BeginTransactionAsync()) { user.IsDeleted = true; user.DeletionTime = DateTime.UtcNow; _sqlServerContext.Update(user); await _sqlServerContext.SaveChangesAsync(); await (await transaction).CommitAsync(); deleted = true; } return deleted; } }