From 513f57ec72af846c34401172f8df2dd7417fe6b0 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sat, 22 Jun 2024 18:42:11 +0400 Subject: [PATCH 1/6] add two factor auth (API) --- BusinessLogic/BusinessLogic/UserLogic.cs | 17 +++++++- .../Mail/MailTemplates/MailTwoFactorCode.cs | 21 ++++++++++ BusinessLogic/Tools/TwoFactorAuthService.cs | 42 +++++++++++++++++++ .../ITwoFactorAuthService.cs | 15 +++++++ .../BusinessLogicContracts/IUserLogic.cs | 20 +++++---- RestAPI/Controllers/UserController.cs | 16 +++++++ RestAPI/Program.cs | 19 +++++---- 7 files changed, 131 insertions(+), 19 deletions(-) create mode 100644 BusinessLogic/Tools/Mail/MailTemplates/MailTwoFactorCode.cs create mode 100644 BusinessLogic/Tools/TwoFactorAuthService.cs create mode 100644 Contracts/BusinessLogicContracts/ITwoFactorAuthService.cs diff --git a/BusinessLogic/BusinessLogic/UserLogic.cs b/BusinessLogic/BusinessLogic/UserLogic.cs index ee6b806..0ccec27 100644 --- a/BusinessLogic/BusinessLogic/UserLogic.cs +++ b/BusinessLogic/BusinessLogic/UserLogic.cs @@ -25,11 +25,14 @@ namespace BusinessLogic.BusinessLogic { private readonly ILogger _logger; private readonly IUserStorage _userStorage; + private readonly ITwoFactorAuthService _twoFactorAuthService; - public UserLogic(ILogger logger, IUserStorage userStorage) + public UserLogic(ILogger logger, IUserStorage userStorage, + ITwoFactorAuthService twoFactorAuthService) { _logger = logger; _userStorage = userStorage; + _twoFactorAuthService = twoFactorAuthService; } public string Create(UserBindingModel model) @@ -53,6 +56,9 @@ namespace BusinessLogic.BusinessLogic MailSender.Send(new MailRegistration(user)); + string code = _twoFactorAuthService.GenerateCode(); + MailSender.Send(new MailTwoFactorCode(user, code)); + return JwtProvider.Generate(user); } @@ -132,6 +138,10 @@ namespace BusinessLogic.BusinessLogic { throw new AccountException("The passwords don't match."); } + + string code = _twoFactorAuthService.GenerateCode(); + MailSender.Send(new MailTwoFactorCode(user, code)); + return JwtProvider.Generate(user); } @@ -166,5 +176,10 @@ namespace BusinessLogic.BusinessLogic throw new AccountException("The email is not valid."); } } + + public bool VerifyCode(string code) + { + return _twoFactorAuthService.Verify(code); + } } } \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailTemplates/MailTwoFactorCode.cs b/BusinessLogic/Tools/Mail/MailTemplates/MailTwoFactorCode.cs new file mode 100644 index 0000000..6c5e4d4 --- /dev/null +++ b/BusinessLogic/Tools/Mail/MailTemplates/MailTwoFactorCode.cs @@ -0,0 +1,21 @@ +using Contracts.BindingModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.Tools.Mail.MailTemplates +{ + public class MailTwoFactorCode : Mail + { + public MailTwoFactorCode(UserBindingModel user, string code) + { + To = [user.Email]; + Title = "Ваш код для подтверждения"; + Body = $"Здравствуйте, {user.SecondName} {user.FirstName}! Вот Ваш код для подтверждения:\n" + + $"{code}\n" + + $"Если это не Вы, игноритруйте это сообщение."; + } + } +} \ No newline at end of file diff --git a/BusinessLogic/Tools/TwoFactorAuthService.cs b/BusinessLogic/Tools/TwoFactorAuthService.cs new file mode 100644 index 0000000..5ab72d6 --- /dev/null +++ b/BusinessLogic/Tools/TwoFactorAuthService.cs @@ -0,0 +1,42 @@ +using Contracts.BusinessLogicContracts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.Tools +{ + public class TwoFactorAuthService : ITwoFactorAuthService + { + private string _code = string.Empty; + private const int LENGTH_CODE = 5; + + public string GenerateCode() + { + _code = _getCode(LENGTH_CODE); + return _code; + } + + private string _getCode(int length) + { + string res = ""; + var rand = new Random(); + for (int i = 0; i < length; i++) + { + res += rand.Next(0, 9).ToString(); + } + return res; + } + + public bool Verify(string code) + { + if (_code == string.Empty) + { + throw new Exception("Source code is not generated."); + } + + return _code == code; + } + } +} \ No newline at end of file diff --git a/Contracts/BusinessLogicContracts/ITwoFactorAuthService.cs b/Contracts/BusinessLogicContracts/ITwoFactorAuthService.cs new file mode 100644 index 0000000..61cea92 --- /dev/null +++ b/Contracts/BusinessLogicContracts/ITwoFactorAuthService.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.BusinessLogicContracts +{ + public interface ITwoFactorAuthService + { + string GenerateCode(); + + bool Verify(string code); + } +} \ No newline at end of file diff --git a/Contracts/BusinessLogicContracts/IUserLogic.cs b/Contracts/BusinessLogicContracts/IUserLogic.cs index 664b346..802f723 100644 --- a/Contracts/BusinessLogicContracts/IUserLogic.cs +++ b/Contracts/BusinessLogicContracts/IUserLogic.cs @@ -10,18 +10,20 @@ using System.Threading.Tasks; namespace Contracts.BusinessLogicContracts { - public interface IUserLogic - { - string Login(string email, string password); + public interface IUserLogic + { + string Login(string email, string password); - string Create(UserBindingModel model); + bool VerifyCode(string code); - UserViewModel Update(UserBindingModel model); + string Create(UserBindingModel model); - UserViewModel ReadElement(UserSearchModel model); + UserViewModel Update(UserBindingModel model); - IEnumerable ReadElements(UserSearchModel? model); + UserViewModel ReadElement(UserSearchModel model); - UserViewModel Delete(UserSearchModel model); - } + IEnumerable ReadElements(UserSearchModel? model); + + UserViewModel Delete(UserSearchModel model); + } } \ No newline at end of file diff --git a/RestAPI/Controllers/UserController.cs b/RestAPI/Controllers/UserController.cs index adae152..0047df3 100644 --- a/RestAPI/Controllers/UserController.cs +++ b/RestAPI/Controllers/UserController.cs @@ -46,6 +46,21 @@ namespace RestAPI.Controllers } } + [HttpPost] + public IResult VerifyCode([FromBody] VerifyCodeData data) + { + try + { + var res = _userLogic.VerifyCode(data.code); + return Results.Ok(res); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error verify code"); + return Results.Problem(ex.Message); + } + } + [HttpPost] public IResult Registration([FromBody] UserBindingModel model) { @@ -128,4 +143,5 @@ namespace RestAPI.Controllers } public record class UserData(string email, string password); + public record class VerifyCodeData(string code); } \ No newline at end of file diff --git a/RestAPI/Program.cs b/RestAPI/Program.cs index 1e6c8aa..f71b32a 100644 --- a/RestAPI/Program.cs +++ b/RestAPI/Program.cs @@ -20,6 +20,7 @@ builder.Logging.AddLog4Net("log4net.config"); builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddSingleton(); builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -34,7 +35,7 @@ builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { - c.SwaggerDoc(VERSION, new OpenApiInfo { Title = TITLE, Version = VERSION }); + c.SwaggerDoc(VERSION, new OpenApiInfo { Title = TITLE, Version = VERSION }); }); var app = builder.Build(); @@ -46,15 +47,15 @@ var mailSender = app.Services.GetService(); string? getSection(string section) => builder.Configuration?.GetSection(section)?.Value?.ToString(); jwtProvider?.SetupJwtOptions(new() { - SecretKey = getSection("JwtOptions:SecretKey") ?? string.Empty, - ExpiresHours = Convert.ToInt16(getSection("JwtOptions:ExpiresHours")) + SecretKey = getSection("JwtOptions:SecretKey") ?? string.Empty, + ExpiresHours = Convert.ToInt16(getSection("JwtOptions:ExpiresHours")) }); mailSender?.SetupMailOptions(new() { - Email = getSection("MailOptions:Email") ?? string.Empty, - Password = getSection("MailOptions:Password") ?? string.Empty, - SmtpClientHost = getSection("MailOptions:SmtpClientHost") ?? string.Empty, - SmtpClientPort = Convert.ToInt16(getSection("MailOptions:SmtpClientPort")) + Email = getSection("MailOptions:Email") ?? string.Empty, + Password = getSection("MailOptions:Password") ?? string.Empty, + SmtpClientHost = getSection("MailOptions:SmtpClientHost") ?? string.Empty, + SmtpClientPort = Convert.ToInt16(getSection("MailOptions:SmtpClientPort")) }); #endregion Setup config @@ -62,8 +63,8 @@ mailSender?.SetupMailOptions(new() // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint($"/swagger/{VERSION}/swagger.json", $"{TITLE} {VERSION}")); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint($"/swagger/{VERSION}/swagger.json", $"{TITLE} {VERSION}")); } app.UseHttpsRedirection(); From f122886b7e8d4d6c181f82756027aabbcce69ee0 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sat, 22 Jun 2024 18:42:35 +0400 Subject: [PATCH 2/6] fix role storage --- DatabaseImplement/Implements/RoleStorage.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DatabaseImplement/Implements/RoleStorage.cs b/DatabaseImplement/Implements/RoleStorage.cs index a1d8e63..390848b 100644 --- a/DatabaseImplement/Implements/RoleStorage.cs +++ b/DatabaseImplement/Implements/RoleStorage.cs @@ -32,7 +32,7 @@ namespace DatabaseImplement.Implements public RoleBindingModel? GetElement(RoleSearchModel model) { - if (model.Id is null && string.IsNullOrWhiteSpace(model.Name)) + if (model.Id is null && string.IsNullOrWhiteSpace(model?.Name)) { return null; } @@ -46,7 +46,7 @@ namespace DatabaseImplement.Implements public IEnumerable GetList(RoleSearchModel? model) { var context = new Database(); - if (model is null && string.IsNullOrWhiteSpace(model.Name)) + if (model is null && string.IsNullOrWhiteSpace(model?.Name)) { return context.Roles.Select(r => r.GetBindingModel()); } From 1c229ab7148ffbf318bdb91d646ee3fd27f6bd04 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sat, 22 Jun 2024 18:43:07 +0400 Subject: [PATCH 3/6] add two factor auth (Web) --- WebApp/Pages/Login.cshtml.cs | 4 +- WebApp/Pages/SignUp.cshtml.cs | 4 +- WebApp/Pages/TwoFactor.cshtml | 128 +++++++++++++++++++++++++++++++ WebApp/Pages/TwoFactor.cshtml.cs | 35 +++++++++ 4 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 WebApp/Pages/TwoFactor.cshtml create mode 100644 WebApp/Pages/TwoFactor.cshtml.cs diff --git a/WebApp/Pages/Login.cshtml.cs b/WebApp/Pages/Login.cshtml.cs index 4a53a79..46144ad 100644 --- a/WebApp/Pages/Login.cshtml.cs +++ b/WebApp/Pages/Login.cshtml.cs @@ -20,9 +20,9 @@ namespace WebApp.Pages throw new Exception("Something wrong LOL!"); } - this.SetJWT((string)response); + TempData["jwt"] = (string)response; - return RedirectToPage("Index"); + return RedirectToPage("TwoFactor"); } } } \ No newline at end of file diff --git a/WebApp/Pages/SignUp.cshtml.cs b/WebApp/Pages/SignUp.cshtml.cs index 727a532..5362104 100644 --- a/WebApp/Pages/SignUp.cshtml.cs +++ b/WebApp/Pages/SignUp.cshtml.cs @@ -30,9 +30,9 @@ namespace WebApp.Pages throw new Exception("Something wrong LOL!"); } - this.SetJWT((string)response); + TempData["jwt"] = (string)response; - return RedirectToPage("Index"); + return RedirectToPage("TwoFactor"); } } } \ No newline at end of file diff --git a/WebApp/Pages/TwoFactor.cshtml b/WebApp/Pages/TwoFactor.cshtml new file mode 100644 index 0000000..c0337fa --- /dev/null +++ b/WebApp/Pages/TwoFactor.cshtml @@ -0,0 +1,128 @@ +@page +@model WebApp.Pages.TwoFactorModel + +
+ +
+ + + +
+
+
+ ionicons-v5-g +
+

+ 2-step verification +

+

+ We sent a verification code to your email. +

+

+ Please enter the code in the field below. +

+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +

+ Didn't receive it? + + Resend code + +

+
+
\ No newline at end of file diff --git a/WebApp/Pages/TwoFactor.cshtml.cs b/WebApp/Pages/TwoFactor.cshtml.cs new file mode 100644 index 0000000..058e2d6 --- /dev/null +++ b/WebApp/Pages/TwoFactor.cshtml.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace WebApp.Pages +{ + public class TwoFactorModel : PageModel + { + [BindProperty] + public int[] Code { get; set; } = []; + + public void OnGet() + { + } + + public IActionResult OnPost(string[] code) + { + var stringCode = string.Join(string.Empty, Code); + if (string.IsNullOrEmpty(stringCode)) + { + throw new Exception("Looo"); + } + var response = (string)APIClient.PostRequest("user/verifycode", new { code = stringCode }); + var isCorrect = Convert.ToBoolean(response); + if (isCorrect) + { + this.SetJWT((string)TempData["jwt"]); + return RedirectToPage("Index"); + } + else + { + throw new Exception("Wrong code! Please retry"); + } + } + } +} \ No newline at end of file From 536a8bfe913a481252e40186d15df0b295464bfa Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sat, 22 Jun 2024 21:15:11 +0400 Subject: [PATCH 4/6] add mail sending policy for user (API) --- BusinessLogic/Tools/Mail/Mail.cs | 13 +- BusinessLogic/Tools/Mail/MailSender.cs | 60 ++++--- .../Mail/MailTemplates/MailRegistration.cs | 24 +-- Contracts/BindingModels/UserBindingModel.cs | 23 +-- Contracts/Converters/UserConverter.cs | 44 ++--- Contracts/SearchModels/UserSearchModel.cs | 11 +- Contracts/ViewModels/UserViewModel.cs | 19 +- DataModels/Models/IUser.cs | 17 +- DatabaseImplement/Implements/UserStorage.cs | 165 +++++++++--------- DatabaseImplement/Models/User.cs | 120 +++++++------ 10 files changed, 258 insertions(+), 238 deletions(-) diff --git a/BusinessLogic/Tools/Mail/Mail.cs b/BusinessLogic/Tools/Mail/Mail.cs index 5adba23..44cc1a8 100644 --- a/BusinessLogic/Tools/Mail/Mail.cs +++ b/BusinessLogic/Tools/Mail/Mail.cs @@ -6,10 +6,11 @@ using System.Threading.Tasks; namespace BusinessLogic.Tools.Mail { - public class Mail - { - public IEnumerable To { get; set; } = null!; - public string Title { get; set; } = null!; - public string Body { get; set; } = null!; - } + public class Mail + { + public IEnumerable To { get; set; } = null!; + public string Title { get; set; } = null!; + public string Body { get; set; } = null!; + public bool IsSendable { get; set; } = true; + } } \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailSender.cs b/BusinessLogic/Tools/Mail/MailSender.cs index 329b392..3df9c48 100644 --- a/BusinessLogic/Tools/Mail/MailSender.cs +++ b/BusinessLogic/Tools/Mail/MailSender.cs @@ -8,39 +8,41 @@ using System.Threading.Tasks; namespace BusinessLogic.Tools.Mail { - public class MailSender - { - private static string _email; - private static string _password; - private static string _smtpClientHost; - private static short _smtpClientPort; + public class MailSender + { + private static string _email; + private static string _password; + private static string _smtpClientHost; + private static short _smtpClientPort; - public void SetupMailOptions(MailOptions options) - { - _email = options.Email; - _password = options.Password; - _smtpClientHost = options.SmtpClientHost; - _smtpClientPort = options.SmtpClientPort; - } + public void SetupMailOptions(MailOptions options) + { + _email = options.Email; + _password = options.Password; + _smtpClientHost = options.SmtpClientHost; + _smtpClientPort = options.SmtpClientPort; + } - public static void Send(Mail mail) - { - using SmtpClient client = new SmtpClient(_smtpClientHost, _smtpClientPort); - client.Credentials = new NetworkCredential(_email, _password); - client.EnableSsl = true; + public static void Send(Mail mail) + { + if (!mail.IsSendable) return; - using MailMessage message = new MailMessage(); + using SmtpClient client = new SmtpClient(_smtpClientHost, _smtpClientPort); + client.Credentials = new NetworkCredential(_email, _password); + client.EnableSsl = true; - message.From = new MailAddress(_email); - foreach (string to in mail.To) - { - message.To.Add(to); - } + using MailMessage message = new MailMessage(); - message.Subject = mail.Title; - message.Body = mail.Body; + message.From = new MailAddress(_email); + foreach (string to in mail.To) + { + message.To.Add(to); + } - client.Send(message); - } - } + message.Subject = mail.Title; + message.Body = mail.Body; + + client.Send(message); + } + } } \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs b/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs index fdd33d9..3a6c2dc 100644 --- a/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs +++ b/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs @@ -8,14 +8,18 @@ using System.Threading.Tasks; namespace BusinessLogic.Tools.Mail.MailTemplates { - public class MailRegistration : Mail - { - public MailRegistration(UserBindingModel user) - { - To = [user.Email]; - Title = "Приветствуем Вас на нашем сайте!"; - Body = $"Спасибо, {user.SecondName} {user.FirstName}, что выбрали НАС.\n" + - $"Надеемся, что Вам что-то уже приглянулось!"; - } - } + public class MailRegistration : Mail + { + public MailRegistration(UserBindingModel user) + { + if (user.OnlyImportantMails) + { + IsSendable = false; + } + To = [user.Email]; + Title = "Приветствуем Вас на нашем сайте!"; + Body = $"Спасибо, {user.SecondName} {user.FirstName}, что выбрали НАС.\n" + + $"Надеемся, что Вам что-то уже приглянулось!"; + } + } } \ No newline at end of file diff --git a/Contracts/BindingModels/UserBindingModel.cs b/Contracts/BindingModels/UserBindingModel.cs index d5e19ae..6c192d6 100644 --- a/Contracts/BindingModels/UserBindingModel.cs +++ b/Contracts/BindingModels/UserBindingModel.cs @@ -6,15 +6,16 @@ using System.Threading.Tasks; namespace Contracts.BindingModels { - public class UserBindingModel - { - public Guid Id { get; set; } - public string FirstName { get; set; } = string.Empty; - public string SecondName { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public string PasswordHash { get; set; } = string.Empty; - public string? Password { get; set; } - public DateTime Birthday { get; set; } - public RoleBindingModel Role { get; set; } = null!; - } + public class UserBindingModel + { + public Guid Id { get; set; } + public string FirstName { get; set; } = string.Empty; + public string SecondName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public string PasswordHash { get; set; } = string.Empty; + public string? Password { get; set; } + public DateTime Birthday { get; set; } + public bool OnlyImportantMails { get; set; } + public RoleBindingModel Role { get; set; } = null!; + } } \ No newline at end of file diff --git a/Contracts/Converters/UserConverter.cs b/Contracts/Converters/UserConverter.cs index b951d11..ae6fd8a 100644 --- a/Contracts/Converters/UserConverter.cs +++ b/Contracts/Converters/UserConverter.cs @@ -8,26 +8,28 @@ using System.Threading.Tasks; namespace Contracts.Converters { - public static class UserConverter - { - public static UserViewModel ToView(UserBindingModel model) => new() - { - Id = model.Id, - FirstName = model.FirstName, - SecondName = model.SecondName, - Email = model.Email, - Birthday = model.Birthday, - Role = RoleConverter.ToView(model.Role), - }; + public static class UserConverter + { + public static UserViewModel ToView(UserBindingModel model) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + Birthday = model.Birthday, + OnlyImportantMails = model.OnlyImportantMails, + Role = RoleConverter.ToView(model.Role), + }; - public static UserBindingModel ToBinding(UserViewModel model) => new() - { - Id = model.Id, - FirstName = model.FirstName, - SecondName = model.SecondName, - Email = model.Email, - Birthday = model.Birthday, - Role = RoleConverter.ToBinding(model.Role), - }; - } + public static UserBindingModel ToBinding(UserViewModel model) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + Birthday = model.Birthday, + OnlyImportantMails = model.OnlyImportantMails, + Role = RoleConverter.ToBinding(model.Role), + }; + } } \ No newline at end of file diff --git a/Contracts/SearchModels/UserSearchModel.cs b/Contracts/SearchModels/UserSearchModel.cs index 2df2131..37271fc 100644 --- a/Contracts/SearchModels/UserSearchModel.cs +++ b/Contracts/SearchModels/UserSearchModel.cs @@ -6,9 +6,10 @@ using System.Threading.Tasks; namespace Contracts.SearchModels { - public class UserSearchModel - { - public Guid? Id { get; set; } - public string? Email { get; set; } - } + public class UserSearchModel + { + public Guid? Id { get; set; } + public string? Email { get; set; } + public bool? OnlyImportantMails { get; set; } + } } \ No newline at end of file diff --git a/Contracts/ViewModels/UserViewModel.cs b/Contracts/ViewModels/UserViewModel.cs index 6174d56..6b3ccd4 100644 --- a/Contracts/ViewModels/UserViewModel.cs +++ b/Contracts/ViewModels/UserViewModel.cs @@ -6,13 +6,14 @@ using System.Threading.Tasks; namespace Contracts.ViewModels { - public class UserViewModel - { - public Guid Id { get; set; } - public string FirstName { get; set; } = string.Empty; - public string SecondName { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public DateTime Birthday { get; set; } - public RoleViewModel Role { get; set; } = null!; - } + public class UserViewModel + { + public Guid Id { get; set; } + public string FirstName { get; set; } = string.Empty; + public string SecondName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public DateTime Birthday { get; set; } + public bool OnlyImportantMails { get; set; } + public RoleViewModel Role { get; set; } = null!; + } } \ No newline at end of file diff --git a/DataModels/Models/IUser.cs b/DataModels/Models/IUser.cs index 9eb2ef6..fb5020d 100644 --- a/DataModels/Models/IUser.cs +++ b/DataModels/Models/IUser.cs @@ -6,12 +6,13 @@ using System.Threading.Tasks; namespace DataModels.Models { - public interface IUser : IId - { - string FirstName { get; } - string SecondName { get; } - string PasswordHash { get; } - string Email { get; } - DateTime Birthday { get; } - } + public interface IUser : IId + { + string FirstName { get; } + string SecondName { get; } + string PasswordHash { get; } + string Email { get; } + DateTime Birthday { get; } + bool OnlyImportantMails { get; } + } } \ No newline at end of file diff --git a/DatabaseImplement/Implements/UserStorage.cs b/DatabaseImplement/Implements/UserStorage.cs index fdbdf0a..2755bbf 100644 --- a/DatabaseImplement/Implements/UserStorage.cs +++ b/DatabaseImplement/Implements/UserStorage.cs @@ -12,98 +12,99 @@ using System.Threading.Tasks; namespace DatabaseImplement.Implements { - public class UserStorage : IUserStorage - { - public UserBindingModel? Delete(UserSearchModel model) - { - if (model.Id is null && model.Email is null) - { - return null; - } + public class UserStorage : IUserStorage + { + public UserBindingModel? Delete(UserSearchModel model) + { + if (model.Id is null && model.Email is null) + { + return null; + } - var context = new Database(); - var user = context.Users.FirstOrDefault(u => - (model.Id.HasValue && u.Id == model.Id) - || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))); + var context = new Database(); + var user = context.Users.FirstOrDefault(u => + (model.Id.HasValue && u.Id == model.Id) + || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))); - if (user is null) - { - return null; - } - context.Remove(user); - context.SaveChanges(); + if (user is null) + { + return null; + } + context.Remove(user); + context.SaveChanges(); - return user.GetBindingModel(); - } + return user.GetBindingModel(); + } - public UserBindingModel? GetElement(UserSearchModel model) - { - if (model.Id is null && model.Email is null) - { - return null; - } - var context = new Database(); - return context.Users - .Include(u => u.Role) - .FirstOrDefault(u => - (model.Id.HasValue && u.Id == model.Id) - || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))) - ?.GetBindingModel(); - } + public UserBindingModel? GetElement(UserSearchModel model) + { + if (model.Id is null && model.Email is null) + { + return null; + } + var context = new Database(); + return context.Users + .Include(u => u.Role) + .FirstOrDefault(u => + (model.Id.HasValue && u.Id == model.Id) + || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))) + ?.GetBindingModel(); + } - public IEnumerable GetList(UserSearchModel? model) - { - var context = new Database(); - if (model is null) - { - return context.Users - .Include(u => u.Role) - .Select(r => r.GetBindingModel()); - } - if (model.Id is null && model.Email is null) - { - return []; - } - return context.Users - .Where(u => - (model.Id.HasValue && u.Id == model.Id) - || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))) - .Include(u => u.Role) - .Select(r => r.GetBindingModel()); - } + public IEnumerable GetList(UserSearchModel? model) + { + var context = new Database(); + if (model is null) + { + return context.Users + .Include(u => u.Role) + .Select(r => r.GetBindingModel()); + } + if (model.Id is null && model.Email is null && !model.OnlyImportantMails is null) + { + return []; + } + return context.Users + .Where(u => + (model.Id.HasValue && u.Id == model.Id) + || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email)) + || (model.OnlyImportantMails.HasValue && u.OnlyImportantMails == model.OnlyImportantMails)) + .Include(u => u.Role) + .Select(r => r.GetBindingModel()); + } - public UserBindingModel? Insert(UserBindingModel model) - { - var context = new Database(); - var role = context.Roles.FirstOrDefault(r => r.Id == model.Role.Id); - if (role is null) - { - return null; - } - var newUser = Models.User.ToUserFromBinding(model, role); + public UserBindingModel? Insert(UserBindingModel model) + { + var context = new Database(); + var role = context.Roles.FirstOrDefault(r => r.Id == model.Role.Id); + if (role is null) + { + return null; + } + var newUser = Models.User.ToUserFromBinding(model, role); - context.Users.Add(newUser); - context.SaveChanges(); + context.Users.Add(newUser); + context.SaveChanges(); - return newUser.GetBindingModel(); - } + return newUser.GetBindingModel(); + } - public UserBindingModel? Update(UserBindingModel model) - { - var context = new Database(); - var user = context.Users - .FirstOrDefault(u => u.Id == model.Id); - var role = context.Roles.FirstOrDefault(r => r.Id == model.Role.Id); + public UserBindingModel? Update(UserBindingModel model) + { + var context = new Database(); + var user = context.Users + .FirstOrDefault(u => u.Id == model.Id); + var role = context.Roles.FirstOrDefault(r => r.Id == model.Role.Id); - if (user is null || role is null) - { - return null; - } + if (user is null || role is null) + { + return null; + } - user.Update(model, role); + user.Update(model, role); - context.SaveChanges(); - return user.GetBindingModel(); - } - } + context.SaveChanges(); + return user.GetBindingModel(); + } + } } \ No newline at end of file diff --git a/DatabaseImplement/Models/User.cs b/DatabaseImplement/Models/User.cs index a2f2ecf..4de0fd1 100644 --- a/DatabaseImplement/Models/User.cs +++ b/DatabaseImplement/Models/User.cs @@ -4,6 +4,7 @@ using Contracts.ViewModels; using DataModels.Models; using System; using System.Collections.Generic; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; @@ -11,72 +12,77 @@ using System.Threading.Tasks; namespace DatabaseImplement.Models { - public class User : IUser - { - public Guid Id { get; set; } + public class User : IUser + { + public Guid Id { get; set; } - [Required] - public string FirstName { get; set; } = string.Empty; + [Required] + public string FirstName { get; set; } = string.Empty; - [Required] - public string SecondName { get; set; } = string.Empty; + [Required] + public string SecondName { get; set; } = string.Empty; - [Required] - public string PasswordHash { get; set; } = string.Empty; + [Required] + public string PasswordHash { get; set; } = string.Empty; - [Required] - public string Email { get; set; } = string.Empty; + [Required] + public string Email { get; set; } = string.Empty; - [Required] - public DateTime Birthday { get; set; } + [Required] + public DateTime Birthday { get; set; } - public Role? Role { get; set; } + public Role? Role { get; set; } - public UserBindingModel GetBindingModel() => new() - { - Id = Id, - FirstName = FirstName, - SecondName = SecondName, - Email = Email, - PasswordHash = PasswordHash, - Birthday = Birthday, - Role = Role?.GetBindingModel() ?? new() - }; + public bool OnlyImportantMails { get; set; } = false; - public static User ToUserFromView(UserViewModel model, Role role) => new() - { - Id = model.Id, - FirstName = model.FirstName, - SecondName = model.SecondName, - Email = model.Email, - Birthday = model.Birthday, - Role = role - }; + public UserBindingModel GetBindingModel() => new() + { + Id = Id, + FirstName = FirstName, + SecondName = SecondName, + Email = Email, + PasswordHash = PasswordHash, + Birthday = Birthday, + OnlyImportantMails = OnlyImportantMails, + Role = Role?.GetBindingModel() ?? new() + }; - public static User ToUserFromBinding(UserBindingModel model, Role role) => new() - { - Id = model.Id, - FirstName = model.FirstName, - SecondName = model.SecondName, - Email = model.Email, - PasswordHash = model.PasswordHash, - Birthday = model.Birthday, - Role = role - }; + public static User ToUserFromView(UserViewModel model, Role role) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + Birthday = model.Birthday, + Role = role + }; - public void Update(UserBindingModel model, Role role) - { - if (model is null) - { - throw new ArgumentNullException("Update user: binding model is null"); - } + public static User ToUserFromBinding(UserBindingModel model, Role role) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + PasswordHash = model.PasswordHash, + Birthday = model.Birthday, + OnlyImportantMails = model.OnlyImportantMails, + Role = role + }; - Email = model.Email; - FirstName = model.FirstName; - SecondName = model.SecondName; - PasswordHash = model.PasswordHash; - Birthday = model.Birthday; - Role = role; - } - } + public void Update(UserBindingModel model, Role role) + { + if (model is null) + { + throw new ArgumentNullException("Update user: binding model is null"); + } + + Email = model.Email ?? Email; + FirstName = model.FirstName ?? FirstName; + SecondName = model.SecondName ?? SecondName; + PasswordHash = model.PasswordHash ?? PasswordHash; + Birthday = model.Birthday; + OnlyImportantMails = model.OnlyImportantMails; + Role = role ?? Role; + } + } } \ No newline at end of file From bbe1fba99ee47d69fdab619e52ca2d98970f0236 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sat, 22 Jun 2024 21:15:42 +0400 Subject: [PATCH 5/6] add mail sending policy for user (Web) --- WebApp/Pages/SignUp.cshtml | 7 +++++++ WebApp/Pages/User/Settings.cshtml | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/WebApp/Pages/SignUp.cshtml b/WebApp/Pages/SignUp.cshtml index fb018dc..31c7539 100644 --- a/WebApp/Pages/SignUp.cshtml +++ b/WebApp/Pages/SignUp.cshtml @@ -74,6 +74,13 @@ +
+ + +
+