From d9f3faf92dcd097cc9488df3270110928348a6b4 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Thu, 13 Jun 2024 18:41:59 +0400 Subject: [PATCH 1/3] add jwt and fix user logic (login) --- BusinessLogic/BusinessLogic.csproj | 3 +- BusinessLogic/BusinessLogic/UserLogic.cs | 4 +- BusinessLogic/Tools/JwtProvider.cs | 41 +++++++++++++++++++ .../BusinessLogicContracts/IUserLogic.cs | 2 +- 4 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 BusinessLogic/Tools/JwtProvider.cs diff --git a/BusinessLogic/BusinessLogic.csproj b/BusinessLogic/BusinessLogic.csproj index c105ec7..ceab2d9 100644 --- a/BusinessLogic/BusinessLogic.csproj +++ b/BusinessLogic/BusinessLogic.csproj @@ -7,12 +7,13 @@ - + + diff --git a/BusinessLogic/BusinessLogic/UserLogic.cs b/BusinessLogic/BusinessLogic/UserLogic.cs index b028660..5fa0692 100644 --- a/BusinessLogic/BusinessLogic/UserLogic.cs +++ b/BusinessLogic/BusinessLogic/UserLogic.cs @@ -102,7 +102,7 @@ namespace BusinessLogic.BusinessLogic return UserConverter.ToView(user); } - public UserViewModel Login(string email, string password) + public string Login(string email, string password) { if (email is null) { @@ -120,7 +120,7 @@ namespace BusinessLogic.BusinessLogic { throw new AccountException("The passwords don't match."); } - return UserConverter.ToView(user); + return JwtProvider.Generate(user); } public void _validatePassword(string? password) diff --git a/BusinessLogic/Tools/JwtProvider.cs b/BusinessLogic/Tools/JwtProvider.cs new file mode 100644 index 0000000..e2a8758 --- /dev/null +++ b/BusinessLogic/Tools/JwtProvider.cs @@ -0,0 +1,41 @@ +using Contracts.BindingModels; +using Contracts.ViewModels; +using Microsoft.IdentityModel.Tokens; +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.Tools +{ + public class JwtProvider + { + // TODO: Переместить ключ и время в надежное место + private const string _key = "secretkey_secretkey_secretkey_secretkey"; + + private const int _expiresHours = 24; + + public static string Generate(UserBindingModel user) + { + var signingCredentials = new SigningCredentials( + new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_key)), + SecurityAlgorithms.HmacSha256); + + Claim[] claims = [ + new("userId", user.Id.ToString()), + new("role", user.Role.Name) + ]; + + var token = new JwtSecurityToken(signingCredentials: signingCredentials, + expires: DateTime.UtcNow.AddHours(_expiresHours), + claims: claims); + + var stringToken = new JwtSecurityTokenHandler().WriteToken(token); + return stringToken; + } + } +} \ No newline at end of file diff --git a/Contracts/BusinessLogicContracts/IUserLogic.cs b/Contracts/BusinessLogicContracts/IUserLogic.cs index fdcc510..dc5c87a 100644 --- a/Contracts/BusinessLogicContracts/IUserLogic.cs +++ b/Contracts/BusinessLogicContracts/IUserLogic.cs @@ -12,7 +12,7 @@ namespace Contracts.BusinessLogicContracts { public interface IUserLogic { - UserViewModel Login(string email, string password); + string Login(string email, string password); UserViewModel Create(UserBindingModel model); -- 2.25.1 From 040d276d5bdac502c0740654a552f3fb35fcebc4 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sat, 15 Jun 2024 00:01:39 +0400 Subject: [PATCH 2/3] fix config jwt provider --- BusinessLogic/Tools/JwtOptions.cs | 14 ++++++++++++++ BusinessLogic/Tools/JwtProvider.cs | 11 ++++++++--- RestAPI/Program.cs | 19 +++++++++++++++++++ RestAPI/appsettings.json | 6 +++++- 4 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 BusinessLogic/Tools/JwtOptions.cs diff --git a/BusinessLogic/Tools/JwtOptions.cs b/BusinessLogic/Tools/JwtOptions.cs new file mode 100644 index 0000000..7327dd0 --- /dev/null +++ b/BusinessLogic/Tools/JwtOptions.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.Tools +{ + public class JwtOptions + { + public string SecretKey { get; set; } = null!; + public short ExpiresHours { get; set; } + } +} \ No newline at end of file diff --git a/BusinessLogic/Tools/JwtProvider.cs b/BusinessLogic/Tools/JwtProvider.cs index e2a8758..525d74d 100644 --- a/BusinessLogic/Tools/JwtProvider.cs +++ b/BusinessLogic/Tools/JwtProvider.cs @@ -14,10 +14,9 @@ namespace BusinessLogic.Tools { public class JwtProvider { - // TODO: Переместить ключ и время в надежное место - private const string _key = "secretkey_secretkey_secretkey_secretkey"; + private static string _key; - private const int _expiresHours = 24; + private static int _expiresHours; public static string Generate(UserBindingModel user) { @@ -37,5 +36,11 @@ namespace BusinessLogic.Tools var stringToken = new JwtSecurityTokenHandler().WriteToken(token); return stringToken; } + + public void SetupJwtOptions(JwtOptions options) + { + _key = options.SecretKey; + _expiresHours = options.ExpiresHours; + } } } \ No newline at end of file diff --git a/RestAPI/Program.cs b/RestAPI/Program.cs index 6fbbd7c..208766e 100644 --- a/RestAPI/Program.cs +++ b/RestAPI/Program.cs @@ -1,9 +1,12 @@ using BusinessLogic.BusinessLogic; +using BusinessLogic.Tools; +using BusinessLogic.Tools.Mail; using Contracts.BusinessLogicContracts; using Contracts.StorageContracts; using DatabaseImplement.Implements; using Microsoft.OpenApi.Models; using System; +using System.Net.Mail; const string VERSION = "v1"; const string TITLE = "21GunsRestAPI"; @@ -21,6 +24,9 @@ builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); + #endregion DI builder.Services.AddControllers(); @@ -32,6 +38,19 @@ builder.Services.AddSwaggerGen(c => }); var app = builder.Build(); +var jwtProvider = app.Services.GetService(); +var mailSender = app.Services.GetService(); + +#region Setup config + +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")) +}); + +#endregion Setup config // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) diff --git a/RestAPI/appsettings.json b/RestAPI/appsettings.json index 10f68b8..995bec1 100644 --- a/RestAPI/appsettings.json +++ b/RestAPI/appsettings.json @@ -5,5 +5,9 @@ "Microsoft.AspNetCore": "Warning" } }, + "JwtOptions": { + "SecretKey": "secretkey_secretkey_secretkey_secretkey", + "ExpiresHours": 24 + }, "AllowedHosts": "*" -} +} \ No newline at end of file -- 2.25.1 From 64cb4f1ac9e8198e2a02b66519805e48d00872a4 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Sat, 15 Jun 2024 01:51:32 +0400 Subject: [PATCH 3/3] add mail sender and basic mail templates --- BusinessLogic/BusinessLogic/UserLogic.cs | 8 ++++ BusinessLogic/Tools/Mail/Mail.cs | 15 ++++++ BusinessLogic/Tools/Mail/MailOptions.cs | 16 +++++++ BusinessLogic/Tools/Mail/MailSender.cs | 46 +++++++++++++++++++ .../Mail/MailTemplates/MailDeleteUser.cs | 20 ++++++++ .../Mail/MailTemplates/MailRegistration.cs | 21 +++++++++ .../Mail/MailTemplates/MailUpdateUserData.cs | 20 ++++++++ RestAPI/Controllers/UserController.cs | 2 - RestAPI/Program.cs | 7 +++ RestAPI/appsettings.json | 6 +++ 10 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 BusinessLogic/Tools/Mail/Mail.cs create mode 100644 BusinessLogic/Tools/Mail/MailOptions.cs create mode 100644 BusinessLogic/Tools/Mail/MailSender.cs create mode 100644 BusinessLogic/Tools/Mail/MailTemplates/MailDeleteUser.cs create mode 100644 BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs create mode 100644 BusinessLogic/Tools/Mail/MailTemplates/MailUpdateUserData.cs diff --git a/BusinessLogic/BusinessLogic/UserLogic.cs b/BusinessLogic/BusinessLogic/UserLogic.cs index 5fa0692..c2f21ba 100644 --- a/BusinessLogic/BusinessLogic/UserLogic.cs +++ b/BusinessLogic/BusinessLogic/UserLogic.cs @@ -1,4 +1,6 @@ using BusinessLogic.Tools; +using BusinessLogic.Tools.Mail; +using BusinessLogic.Tools.Mail.MailTemplates; using Contracts.BindingModels; using Contracts.BusinessLogicContracts; using Contracts.Converters; @@ -39,6 +41,8 @@ namespace BusinessLogic.BusinessLogic throw new Exception("Insert operation failed."); } + MailSender.Send(new MailRegistration(user)); + return UserConverter.ToView(user); } @@ -52,6 +56,7 @@ namespace BusinessLogic.BusinessLogic { throw new ElementNotFoundException(); } + MailSender.Send(new MailDeleteUser(user)); return UserConverter.ToView(user); } @@ -99,6 +104,9 @@ namespace BusinessLogic.BusinessLogic { throw new Exception("Update operation failed."); } + + MailSender.Send(new MailUpdateUserData(user)); + return UserConverter.ToView(user); } diff --git a/BusinessLogic/Tools/Mail/Mail.cs b/BusinessLogic/Tools/Mail/Mail.cs new file mode 100644 index 0000000..5adba23 --- /dev/null +++ b/BusinessLogic/Tools/Mail/Mail.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +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!; + } +} \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailOptions.cs b/BusinessLogic/Tools/Mail/MailOptions.cs new file mode 100644 index 0000000..98a6920 --- /dev/null +++ b/BusinessLogic/Tools/Mail/MailOptions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.Tools.Mail +{ + public class MailOptions + { + public string Email { get; set; } = null!; + public string Password { get; set; } = null!; + public string SmtpClientHost { get; set; } = null!; + public short SmtpClientPort { get; set; } + } +} \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailSender.cs b/BusinessLogic/Tools/Mail/MailSender.cs new file mode 100644 index 0000000..329b392 --- /dev/null +++ b/BusinessLogic/Tools/Mail/MailSender.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Mail; +using System.Text; +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 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; + + using MailMessage message = new MailMessage(); + + message.From = new MailAddress(_email); + foreach (string to in mail.To) + { + message.To.Add(to); + } + + message.Subject = mail.Title; + message.Body = mail.Body; + + client.Send(message); + } + } +} \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailTemplates/MailDeleteUser.cs b/BusinessLogic/Tools/Mail/MailTemplates/MailDeleteUser.cs new file mode 100644 index 0000000..2a1bef8 --- /dev/null +++ b/BusinessLogic/Tools/Mail/MailTemplates/MailDeleteUser.cs @@ -0,0 +1,20 @@ +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 MailDeleteUser : Mail + { + public MailDeleteUser(UserBindingModel user) + { + To = [user.Email]; + Title = "Ваш аккаунт был удален!"; + Body = $"Уважаемый {user.SecondName} {user.FirstName}, Ваш аккаунт был удален навсегда насовсем.\n" + + $"Если это были не Вы... Тут уже ничего не поможет, приносим наши извинения."; + } + } +} \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs b/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs new file mode 100644 index 0000000..fdd33d9 --- /dev/null +++ b/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs @@ -0,0 +1,21 @@ +using Contracts.BindingModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +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" + + $"Надеемся, что Вам что-то уже приглянулось!"; + } + } +} \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailTemplates/MailUpdateUserData.cs b/BusinessLogic/Tools/Mail/MailTemplates/MailUpdateUserData.cs new file mode 100644 index 0000000..4508583 --- /dev/null +++ b/BusinessLogic/Tools/Mail/MailTemplates/MailUpdateUserData.cs @@ -0,0 +1,20 @@ +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 MailUpdateUserData : Mail + { + public MailUpdateUserData(UserBindingModel user) + { + To = [user.Email]; + Title = "Данные пользователя были обновлены!"; + Body = $"Уважаемый {user.SecondName} {user.FirstName}, Ваши данные были обвновлены.\n" + + $"Если это были не Вы, то что поделать, Вам придется менять пароль."; + } + } +} \ No newline at end of file diff --git a/RestAPI/Controllers/UserController.cs b/RestAPI/Controllers/UserController.cs index bbeb162..57d6eee 100644 --- a/RestAPI/Controllers/UserController.cs +++ b/RestAPI/Controllers/UserController.cs @@ -56,13 +56,11 @@ namespace RestAPI.Controllers catch (AccountException ex) { _logger.LogWarning(ex, "Wrong registration data"); - throw; return Results.BadRequest(ex.Message); } catch (Exception ex) { _logger.LogError(ex, "Error create user"); - throw; return Results.Problem(ex.Message); } } diff --git a/RestAPI/Program.cs b/RestAPI/Program.cs index 208766e..1e6c8aa 100644 --- a/RestAPI/Program.cs +++ b/RestAPI/Program.cs @@ -49,6 +49,13 @@ jwtProvider?.SetupJwtOptions(new() 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")) +}); #endregion Setup config diff --git a/RestAPI/appsettings.json b/RestAPI/appsettings.json index 995bec1..51c1d26 100644 --- a/RestAPI/appsettings.json +++ b/RestAPI/appsettings.json @@ -9,5 +9,11 @@ "SecretKey": "secretkey_secretkey_secretkey_secretkey", "ExpiresHours": 24 }, + "MailOptions": { + "Email": "21.guns.site@gmail.com", + "Password": "tiss lpgf nhap qnfc", + "SmtpClientHost": "smtp.gmail.com", + "SmtpClientPort": "587" + }, "AllowedHosts": "*" } \ No newline at end of file -- 2.25.1