diff --git a/.gitignore b/.gitignore index 155c719e64d1fc66acb5aea68cf3b774ba52e040..fdef719deff65f38fa89f5e368c63052fc7514cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -appsettings.Development.json +AuthenticationMicroservice/appsettings.Development.json # Created by https://www.toptal.com/developers/gitignore/api/aspnetcore # Edit at https://www.toptal.com/developers/gitignore?templates=aspnetcore diff --git a/AuthenticationMicroservice.Tests/AuthServiceTests.cs b/AuthenticationMicroservice.Tests/AuthServiceTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..840fbe5c8be949e69be6215fd655b0fc08219c7a --- /dev/null +++ b/AuthenticationMicroservice.Tests/AuthServiceTests.cs @@ -0,0 +1,81 @@ +namespace AuthenticationMicroservice.Tests; + +using AutoMapper; +using Helpers; +using Microsoft.Extensions.Options; +using Models.DTOs; +using Models.Entities; +using Moq; +using Repositories; +using Services; +using Settings; + +public class AuthServiceTests +{ + private readonly IMapper _mapper; + + public AuthServiceTests() + { + _mapper = AutoMapperHelper.CreateTestMapper(); + } + + [Fact] + public async Task TestRegisterUser() + { + var registerRequest = new UserRegisterRequestDTO + { + EmailAddress = "aa03980@surrey.ac.uk", + Password = "Testtest1.", + ConfirmPassword = "Testtest1.", + FirstName = "Adiv", + Surname = "Asif" + }; + + await using var context = DbContextHelper.CreateInMemoryDbContext(); + var userRepo = new BaseRepository<User>(context); + + var userService = new UserService(_mapper, userRepo); + var createdUser = await userService.CreateUser(registerRequest); + Assert.True(createdUser.EmailAddress == "aa03980@surrey.ac.uk"); + } + + [Fact] + public async Task TestRegisterAndLoginUser() + { + var registerRequest = new UserRegisterRequestDTO + { + EmailAddress = "aa03980@surrey.ac.uk", + Password = "Testtest1.", + ConfirmPassword = "Testtest1.", + FirstName = "Adiv", + Surname = "Asif" + }; + + var loginRequest = new UserLoginRequestDTO + { + EmailAddress = "aa03980@surrey.ac.uk", + Password = "Testtest1." + }; + + await using var context = DbContextHelper.CreateInMemoryDbContext(); + var userRepo = new BaseRepository<User>(context); + + var userService = new UserService(_mapper, userRepo); + var createdUser = await userService.CreateUser(registerRequest); + + var mockEmailService = new Mock<IEmailService>(); + var mockRefreshTokenService = new Mock<IRefreshTokenService>(); + mockRefreshTokenService.Setup(r => r.CreateNewTokenForUser(It.IsAny<int>())).ReturnsAsync(new AccessToken{Token = "test"}); + mockRefreshTokenService.Setup(r => r.CreateNewTokenForUser(It.IsAny<User>())).Returns(new AccessToken{Token = "test"}); + mockRefreshTokenService.Setup(r => r.GenerateRefreshToken(It.IsAny<int>())).ReturnsAsync(new RefreshToken{Token = "test"}); + + var frontendString = new FrontendStrings {BaseUrl = "test"}; + var mockOptions = new Mock<IOptions<FrontendStrings>>(); + mockOptions.Setup(s => s.Value).Returns(frontendString); + + var authService = new AuthService(context, mockEmailService.Object, mockRefreshTokenService.Object, mockOptions.Object); + var login = await authService.Authenticate(loginRequest); + + Assert.True(login.UserId == createdUser.Id); + } +} \ No newline at end of file diff --git a/AuthenticationMicroservice.Tests/AuthenticationMicroservice.Tests.csproj b/AuthenticationMicroservice.Tests/AuthenticationMicroservice.Tests.csproj new file mode 100644 index 0000000000000000000000000000000000000000..8982fe31af8f12eebe663d69e5eb4c22f7862b3b --- /dev/null +++ b/AuthenticationMicroservice.Tests/AuthenticationMicroservice.Tests.csproj @@ -0,0 +1,25 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + + <IsPackable>false</IsPackable> + <IsTestProject>true</IsTestProject> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="xunit" Version="2.4.1"/> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0"/> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\AuthenticationMicroservice\AuthenticationMicroservice.csproj"/> + </ItemGroup> + +</Project> diff --git a/AuthenticationMicroservice.Tests/Helpers/AutoMapperHelper.cs b/AuthenticationMicroservice.Tests/Helpers/AutoMapperHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..1bb0b4d5bd58dab093a12d8d6a65fcc459e96ebe --- /dev/null +++ b/AuthenticationMicroservice.Tests/Helpers/AutoMapperHelper.cs @@ -0,0 +1,13 @@ +namespace AuthenticationMicroservice.Tests.Helpers; + +using AutoMapper; + +public class AutoMapperHelper +{ + public static IMapper CreateTestMapper() + { + var configuration = new MapperConfiguration(cfg => { cfg.AddProfile<AutoMapperProfile>(); }); + + return configuration.CreateMapper(); + } +} \ No newline at end of file diff --git a/AuthenticationMicroservice.Tests/Helpers/DbContextHelper.cs b/AuthenticationMicroservice.Tests/Helpers/DbContextHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..b9dd55358672a023c8faa65e49b4da3d2864b18e --- /dev/null +++ b/AuthenticationMicroservice.Tests/Helpers/DbContextHelper.cs @@ -0,0 +1,15 @@ +namespace AuthenticationMicroservice.Tests.Helpers; + +using Microsoft.EntityFrameworkCore; +using Models; + +public class DbContextHelper +{ + public static AuthenticationDbContext CreateInMemoryDbContext() + { + var options = new DbContextOptionsBuilder<AuthenticationDbContext>() + .UseInMemoryDatabase(Guid.NewGuid().ToString()).Options; + var context = new AuthenticationDbContext(options); + return context; + } +} \ No newline at end of file diff --git a/AuthenticationMicroservice.Tests/Usings.cs b/AuthenticationMicroservice.Tests/Usings.cs new file mode 100644 index 0000000000000000000000000000000000000000..8c927eb747a6a304db265e6753aee6c47504f604 --- /dev/null +++ b/AuthenticationMicroservice.Tests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/AuthenticationMicroservice.sln b/AuthenticationMicroservice.sln index 4f32aeaaa9a16efe05c35ecb76bcec4732466ce9..faa1d39b0d42eff41e58e6a0f8ec3dead3a35613 100644 --- a/AuthenticationMicroservice.sln +++ b/AuthenticationMicroservice.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.6.33513.286 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AuthenticationMicroservice", "AuthenticationMicroservice.csproj", "{EFB5174F-F711-4272-A765-8147AF59AF34}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AuthenticationMicroservice", "AuthenticationMicroservice\AuthenticationMicroservice.csproj", "{EFB5174F-F711-4272-A765-8147AF59AF34}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AuthenticationMicroservice.Tests", "AuthenticationMicroservice.Tests\AuthenticationMicroservice.Tests.csproj", "{93DC4F65-3B37-466A-BFE0-B96ABCD6CCBD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,10 @@ Global {EFB5174F-F711-4272-A765-8147AF59AF34}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFB5174F-F711-4272-A765-8147AF59AF34}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFB5174F-F711-4272-A765-8147AF59AF34}.Release|Any CPU.Build.0 = Release|Any CPU + {93DC4F65-3B37-466A-BFE0-B96ABCD6CCBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93DC4F65-3B37-466A-BFE0-B96ABCD6CCBD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93DC4F65-3B37-466A-BFE0-B96ABCD6CCBD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93DC4F65-3B37-466A-BFE0-B96ABCD6CCBD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/.dockerignore b/AuthenticationMicroservice/.dockerignore similarity index 100% rename from .dockerignore rename to AuthenticationMicroservice/.dockerignore diff --git a/AuthenticationMicroservice.csproj b/AuthenticationMicroservice/AuthenticationMicroservice.csproj similarity index 88% rename from AuthenticationMicroservice.csproj rename to AuthenticationMicroservice/AuthenticationMicroservice.csproj index 8c6b137d42d8b37ef2005930dd4334a84f2d06b4..fba275bbfd7067a665053004196567b142f0e4c6 100644 --- a/AuthenticationMicroservice.csproj +++ b/AuthenticationMicroservice/AuthenticationMicroservice.csproj @@ -11,10 +11,14 @@ <ItemGroup> <PackageReference Include="AutoMapper" Version="12.0.1"/> - <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0"/> + <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1"/> <PackageReference Include="Azure.Storage.Blobs" Version="12.16.0-beta.1"/> <PackageReference Include="Azure.Storage.Common" Version="12.15.0-beta.1"/> <PackageReference Include="BenchmarkDotNet" Version="0.13.5"/> + <PackageReference Include="coverlet.collector" Version="3.2.0"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> <PackageReference Include="LazyCache" Version="2.4.0"/> <PackageReference Include="LazyCache.AspNetCore" Version="2.4.0"/> <PackageReference Include="MailKit" Version="3.6.0"/> @@ -33,6 +37,7 @@ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1"/> + <PackageReference Include="Moq" Version="4.18.4"/> <PackageReference Include="Newtonsoft.Json" Version="13.0.3"/> <PackageReference Include="Polly" Version="7.2.3"/> <PackageReference Include="SixLabors.ImageSharp" Version="3.0.1"/> diff --git a/AutoMapperProfile.cs b/AuthenticationMicroservice/AutoMapperProfile.cs similarity index 100% rename from AutoMapperProfile.cs rename to AuthenticationMicroservice/AutoMapperProfile.cs diff --git a/Constants.cs b/AuthenticationMicroservice/Constants.cs similarity index 100% rename from Constants.cs rename to AuthenticationMicroservice/Constants.cs diff --git a/Controllers/AuthenticationController.cs b/AuthenticationMicroservice/Controllers/AuthenticationController.cs similarity index 84% rename from Controllers/AuthenticationController.cs rename to AuthenticationMicroservice/Controllers/AuthenticationController.cs index d16d97e5a717d9f074c64c7bed34f87ed9001d44..7986363e08a16d60712e8c99f2d78f5b657348de 100644 --- a/Controllers/AuthenticationController.cs +++ b/AuthenticationMicroservice/Controllers/AuthenticationController.cs @@ -2,16 +2,19 @@ namespace AuthenticationMicroservice.Controllers; using System.Net; using System.Security.Claims; +using System.Text; using Exceptions; using Helpers; using LazyCache; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Middleware; using Models.DTOs; using Models.Entities; using Services; +using Settings; using Swashbuckle.AspNetCore.Annotations; [Route("api/[controller]")] @@ -20,17 +23,19 @@ public class AuthController : BaseAuthController { private readonly IAuthService _authService; private readonly IEmailService _emailService; + private readonly FrontendStrings _frontendStrings; private readonly IAppCache _memoryCache; private readonly IRefreshTokenService _refreshTokenService; private readonly IUserService _userService; public AuthController(IAppCache memoryCache, IAuthService authService, IRefreshTokenService refreshTokenService, - IUserService userService, IEmailService emailService) + IUserService userService, IEmailService emailService, IOptions<FrontendStrings> frontendStrings) { _authService = authService; _memoryCache = memoryCache; _userService = userService; _emailService = emailService; + _frontendStrings = frontendStrings.Value; _refreshTokenService = refreshTokenService; } @@ -211,7 +216,8 @@ public class AuthController : BaseAuthController if (request.EmailAddress != null) _emailService.SendEmail(new EmailDTO { - Subject = "Successfully Registered", Body = "Test", Receiver = request.EmailAddress, + Subject = "Successfully Registered", Body = GetRegisteredUserTableEmail(createdUser), + Receiver = request.EmailAddress, To = request.Username }).FireAndForget(); return Ok(authenticatedUser); @@ -226,11 +232,11 @@ public class AuthController : BaseAuthController [HttpPost("ResendConfirmEmail")] [SwaggerResponse(204)] [SwaggerResponse(400, Type = typeof(ResponseEnvelope<BadRequestObjectResult>))] - public async Task<ActionResult> ResendConfirmEmail(string callbackUrl) + public async Task<ActionResult> ResendConfirmEmail() { try { - await _authService.ResendConfirmEmail(UserId, callbackUrl); + await _authService.ResendConfirmEmail(UserId); } catch (Exception ex) { @@ -305,4 +311,34 @@ public class AuthController : BaseAuthController return BadRequest(ex.Message); } } + + private string GetRegisteredUserTableEmail(User user) + { + var s = new StringBuilder(); + s.Append($"<table {EmailHelper.TableProperties(TableSize.Small)}>"); + s.Append("<thead>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableHeader(2)}>New User Registration</th>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableCell}>First Name</th>"); + s.Append($"<td {EmailHelper.TableCell}>{user.FirstName}</td>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableCell}>Surname</th>"); + s.Append($"<td {EmailHelper.TableCell}>{user.Surname}</td>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableCell}>Username</th>"); + s.Append($"<td {EmailHelper.TableCell}>{user.Username}</td>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<td {EmailHelper.TableFooter(2)}></td>"); + s.Append("</tr>"); + s.Append("</thead>"); + s.Append("</table>"); + s.Append( + $"<p style='font-family: sans-serif'>Please click here to <a href='{_frontendStrings.BaseUrl}/ConfirmEmail?{user.EmailConfirmationToken}'>confirm your email.</a></p>"); + return s.ToString(); + } } \ No newline at end of file diff --git a/Controllers/AuthorizeRolesAttribute.cs b/AuthenticationMicroservice/Controllers/AuthorizeRolesAttribute.cs similarity index 100% rename from Controllers/AuthorizeRolesAttribute.cs rename to AuthenticationMicroservice/Controllers/AuthorizeRolesAttribute.cs diff --git a/Controllers/BaseAuthenticationController.cs b/AuthenticationMicroservice/Controllers/BaseAuthenticationController.cs similarity index 100% rename from Controllers/BaseAuthenticationController.cs rename to AuthenticationMicroservice/Controllers/BaseAuthenticationController.cs diff --git a/Controllers/BaseController.cs b/AuthenticationMicroservice/Controllers/BaseController.cs similarity index 100% rename from Controllers/BaseController.cs rename to AuthenticationMicroservice/Controllers/BaseController.cs diff --git a/Dockerfile b/AuthenticationMicroservice/Dockerfile similarity index 100% rename from Dockerfile rename to AuthenticationMicroservice/Dockerfile diff --git a/Exceptions/AuthenticationException.cs b/AuthenticationMicroservice/Exceptions/AuthenticationException.cs similarity index 100% rename from Exceptions/AuthenticationException.cs rename to AuthenticationMicroservice/Exceptions/AuthenticationException.cs diff --git a/AuthenticationMicroservice/Helpers/EmailHelper.cs b/AuthenticationMicroservice/Helpers/EmailHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..46cc419438594ddc643da3b792ed45feb9af65cc --- /dev/null +++ b/AuthenticationMicroservice/Helpers/EmailHelper.cs @@ -0,0 +1,32 @@ +namespace AuthenticationMicroservice.Helpers; + +public enum TableSize +{ + Small = 500, + Medium = 800, + Large = 1000 +} + +public static class EmailHelper +{ + public const string TableCell = + " style='color:#000; padding-bottom:1%; vertical-align: middle; border-bottom: 1px solid #8D8AD8; border-left: 1px solid #8D8AD8; border-right: 1px solid #8D8AD8;'"; + + public static string TableProperties(TableSize width) + { + return + $"width={width} cellpadding=0 cellspacing=0 border=0 style='text-align: center; font-family: sans-serif'"; + } + + public static string TableHeader(int colspan) + { + return + $"colspan='{colspan}' style='color:#000; background:#8D8AD8; text-align: center; border-bottom: 1px solid #414447; font-weight: 100; padding:8px; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); border-top-left-radius:10px; border-top-right-radius:10px;'"; + } + + public static string TableFooter(int colspan) + { + return + $"colspan='{colspan}' style='color:#000; background:#8D8AD8; text-align: center; font-weight: 100; padding:8px; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); border-bottom-left-radius:10px; border-bottom-right-radius:10px;'"; + } +} \ No newline at end of file diff --git a/Helpers/ResponseOperationFilter.cs b/AuthenticationMicroservice/Helpers/ResponseOperationFilter.cs similarity index 100% rename from Helpers/ResponseOperationFilter.cs rename to AuthenticationMicroservice/Helpers/ResponseOperationFilter.cs diff --git a/Helpers/SecurityHelper.cs b/AuthenticationMicroservice/Helpers/SecurityHelper.cs similarity index 100% rename from Helpers/SecurityHelper.cs rename to AuthenticationMicroservice/Helpers/SecurityHelper.cs diff --git a/Helpers/TaskExtensions.cs b/AuthenticationMicroservice/Helpers/TaskExtensions.cs similarity index 100% rename from Helpers/TaskExtensions.cs rename to AuthenticationMicroservice/Helpers/TaskExtensions.cs diff --git a/Middleware/ExceptionMiddleware.cs b/AuthenticationMicroservice/Middleware/ExceptionMiddleware.cs similarity index 100% rename from Middleware/ExceptionMiddleware.cs rename to AuthenticationMicroservice/Middleware/ExceptionMiddleware.cs diff --git a/Middleware/SwaggerAuthMiddleware.cs b/AuthenticationMicroservice/Middleware/SwaggerAuthMiddleware.cs similarity index 100% rename from Middleware/SwaggerAuthMiddleware.cs rename to AuthenticationMicroservice/Middleware/SwaggerAuthMiddleware.cs diff --git a/Middleware/SwaggerAuthorizeExtensions.cs b/AuthenticationMicroservice/Middleware/SwaggerAuthorizeExtensions.cs similarity index 100% rename from Middleware/SwaggerAuthorizeExtensions.cs rename to AuthenticationMicroservice/Middleware/SwaggerAuthorizeExtensions.cs diff --git a/Migrations/20230325232645_InitialMigration.Designer.cs b/AuthenticationMicroservice/Migrations/20230325232645_InitialMigration.Designer.cs similarity index 100% rename from Migrations/20230325232645_InitialMigration.Designer.cs rename to AuthenticationMicroservice/Migrations/20230325232645_InitialMigration.Designer.cs diff --git a/Migrations/20230325232645_InitialMigration.cs b/AuthenticationMicroservice/Migrations/20230325232645_InitialMigration.cs similarity index 100% rename from Migrations/20230325232645_InitialMigration.cs rename to AuthenticationMicroservice/Migrations/20230325232645_InitialMigration.cs diff --git a/Migrations/20230505160442_RemovedProfilePictures.Designer.cs b/AuthenticationMicroservice/Migrations/20230505160442_RemovedProfilePictures.Designer.cs similarity index 100% rename from Migrations/20230505160442_RemovedProfilePictures.Designer.cs rename to AuthenticationMicroservice/Migrations/20230505160442_RemovedProfilePictures.Designer.cs diff --git a/Migrations/20230505160442_RemovedProfilePictures.cs b/AuthenticationMicroservice/Migrations/20230505160442_RemovedProfilePictures.cs similarity index 100% rename from Migrations/20230505160442_RemovedProfilePictures.cs rename to AuthenticationMicroservice/Migrations/20230505160442_RemovedProfilePictures.cs diff --git a/Migrations/AuthenticationDbContextModelSnapshot.cs b/AuthenticationMicroservice/Migrations/AuthenticationDbContextModelSnapshot.cs similarity index 100% rename from Migrations/AuthenticationDbContextModelSnapshot.cs rename to AuthenticationMicroservice/Migrations/AuthenticationDbContextModelSnapshot.cs diff --git a/Models/AuthenticationDbContext.cs b/AuthenticationMicroservice/Models/AuthenticationDbContext.cs similarity index 100% rename from Models/AuthenticationDbContext.cs rename to AuthenticationMicroservice/Models/AuthenticationDbContext.cs diff --git a/Models/BaseObjects/BaseDTO.cs b/AuthenticationMicroservice/Models/BaseObjects/BaseDTO.cs similarity index 100% rename from Models/BaseObjects/BaseDTO.cs rename to AuthenticationMicroservice/Models/BaseObjects/BaseDTO.cs diff --git a/Models/BaseObjects/BaseEntity.cs b/AuthenticationMicroservice/Models/BaseObjects/BaseEntity.cs similarity index 100% rename from Models/BaseObjects/BaseEntity.cs rename to AuthenticationMicroservice/Models/BaseObjects/BaseEntity.cs diff --git a/Models/DTOs/EmailDTO.cs b/AuthenticationMicroservice/Models/DTOs/EmailDTO.cs similarity index 100% rename from Models/DTOs/EmailDTO.cs rename to AuthenticationMicroservice/Models/DTOs/EmailDTO.cs diff --git a/Models/DTOs/UserDTO.cs b/AuthenticationMicroservice/Models/DTOs/UserDTO.cs similarity index 100% rename from Models/DTOs/UserDTO.cs rename to AuthenticationMicroservice/Models/DTOs/UserDTO.cs diff --git a/Models/Entities/RefreshToken.cs b/AuthenticationMicroservice/Models/Entities/RefreshToken.cs similarity index 100% rename from Models/Entities/RefreshToken.cs rename to AuthenticationMicroservice/Models/Entities/RefreshToken.cs diff --git a/Models/Entities/User.cs b/AuthenticationMicroservice/Models/Entities/User.cs similarity index 100% rename from Models/Entities/User.cs rename to AuthenticationMicroservice/Models/Entities/User.cs diff --git a/Program.cs b/AuthenticationMicroservice/Program.cs similarity index 100% rename from Program.cs rename to AuthenticationMicroservice/Program.cs diff --git a/Properties/launchSettings.json b/AuthenticationMicroservice/Properties/launchSettings.json similarity index 100% rename from Properties/launchSettings.json rename to AuthenticationMicroservice/Properties/launchSettings.json diff --git a/Repositories/BaseRepository.cs b/AuthenticationMicroservice/Repositories/BaseRepository.cs similarity index 100% rename from Repositories/BaseRepository.cs rename to AuthenticationMicroservice/Repositories/BaseRepository.cs diff --git a/Services/AuthService.cs b/AuthenticationMicroservice/Services/AuthService.cs similarity index 73% rename from Services/AuthService.cs rename to AuthenticationMicroservice/Services/AuthService.cs index 3826dd8c3b6326a3e44f91717edb35ba1c938488..f7669749b66afc8f186eede910419a37bd4b9b2f 100644 --- a/Services/AuthService.cs +++ b/AuthenticationMicroservice/Services/AuthService.cs @@ -1,13 +1,16 @@ namespace AuthenticationMicroservice.Services; using System.Net; +using System.Text; using Exceptions; using Helpers; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Models; using Models.DTOs; using Models.Entities; +using Settings; using static Guid; public interface IAuthService @@ -19,7 +22,7 @@ public interface IAuthService Task SetPassword(SetPasswordRequestDTO request); Task ChangePassword(int userId, ChangePasswordRequestDTO request); Task ChangeEmail(int userId, ChangeEmailRequestDTO request); - Task ResendConfirmEmail(int userId, string callbackUrl); + Task ResendConfirmEmail(int userId); Task ConfirmEmailAddress(int userId, string token); Task Logout(int userId); } @@ -28,13 +31,15 @@ public class AuthService : IAuthService { private readonly AuthenticationDbContext _context; private readonly IEmailService _emailService; + private readonly FrontendStrings _frontendStrings; private readonly IRefreshTokenService _refreshTokenService; public AuthService(AuthenticationDbContext context, IEmailService emailService, - IRefreshTokenService refreshTokenService) + IRefreshTokenService refreshTokenService, IOptions<FrontendStrings> frontendStrings) { _context = context; _emailService = emailService; + _frontendStrings = frontendStrings.Value; _refreshTokenService = refreshTokenService; } @@ -135,14 +140,15 @@ public class AuthService : IAuthService await _context.SaveChangesAsync(); } - public async Task ResendConfirmEmail(int userId, string callbackUrl) + public async Task ResendConfirmEmail(int userId) { var user = await _context.User.FirstOrDefaultAsync(u => u.Id == userId); if (user?.EmailConfirmationToken == null) throw new AuthenticationException("User already confirmed.", HttpStatusCode.Unauthorized); _emailService.SendEmail(new EmailDTO { - Subject = "Resend Confirm Email", Body = "Test", To = user.UnconfirmedEmail!, Receiver = user.Username + Subject = "Resend Confirm Email", Body = GetResendConfirmTableEmail(user), To = user.UnconfirmedEmail!, + Receiver = user.Username }) .FireAndForget(); } @@ -158,7 +164,10 @@ public class AuthService : IAuthService _context.User.Update(user); await _context.SaveChangesAsync(); _emailService.SendEmail(new EmailDTO - {Subject = "Forgot Password", Body = "Test", To = request.EmailAddress!, Receiver = user.Username}) + { + Subject = "Forgot Password", Body = GetForgotPasswordTableEmail(user), To = request.EmailAddress!, + Receiver = user.Username + }) .FireAndForget(); } @@ -217,4 +226,64 @@ public class AuthService : IAuthService _context.User.Update(user); await _context.SaveChangesAsync(); } + + private string GetResendConfirmTableEmail(User user) + { + var s = new StringBuilder(); + s.Append($"<table {EmailHelper.TableProperties(TableSize.Small)}>"); + s.Append("<thead>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableHeader(2)}>New User Registration</th>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableCell}>First Name</th>"); + s.Append($"<td {EmailHelper.TableCell}>{user.FirstName}</td>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableCell}>Surname</th>"); + s.Append($"<td {EmailHelper.TableCell}>{user.Surname}</td>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableCell}>Username</th>"); + s.Append($"<td {EmailHelper.TableCell}>{user.Username}</td>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<td {EmailHelper.TableFooter(2)}></td>"); + s.Append("</tr>"); + s.Append("</thead>"); + s.Append("</table>"); + s.Append( + $"<p style='font-family: sans-serif'>Please click here to <a href='{_frontendStrings.BaseUrl}/ConfirmEmail?{user.EmailConfirmationToken}'>confirm your email.</a></p>"); + return s.ToString(); + } + + private string GetForgotPasswordTableEmail(User user) + { + var s = new StringBuilder(); + s.Append($"<table {EmailHelper.TableProperties(TableSize.Small)}>"); + s.Append("<thead>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableHeader(2)}>Password Reset</th>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableCell}>First Name</th>"); + s.Append($"<td {EmailHelper.TableCell}>{user.FirstName}</td>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableCell}>Surname</th>"); + s.Append($"<td {EmailHelper.TableCell}>{user.Surname}</td>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<th {EmailHelper.TableCell}>Username</th>"); + s.Append($"<td {EmailHelper.TableCell}>{user.Username}</td>"); + s.Append("</tr>"); + s.Append("<tr>"); + s.Append($"<td {EmailHelper.TableFooter(2)}></td>"); + s.Append("</tr>"); + s.Append("</thead>"); + s.Append("</table>"); + s.Append( + $"<p style='font-family: sans-serif'>Please click here to <a href='{_frontendStrings.BaseUrl}/ResetPassword?{user.PasswordResetToken}'>reset your password.</a></p>"); + return s.ToString(); + } } \ No newline at end of file diff --git a/Services/EmailService.cs b/AuthenticationMicroservice/Services/EmailService.cs similarity index 96% rename from Services/EmailService.cs rename to AuthenticationMicroservice/Services/EmailService.cs index 48e0904f0069cfc326dbac69f08c4adf3b1b13ba..218ccdb2c39b3e40a282546fa0484d802cebabcb 100644 --- a/Services/EmailService.cs +++ b/AuthenticationMicroservice/Services/EmailService.cs @@ -31,7 +31,7 @@ public class EmailService : IEmailService email.From.Add(new MailboxAddress("aa03980", "smtp.gmail.com")); email.To.Add(new MailboxAddress(emailDTO.Receiver, emailDTO.To)); email.Subject = emailDTO.Subject; - email.Body = new TextPart(TextFormat.Text) {Text = emailDTO.Body}; + email.Body = new TextPart(TextFormat.Html) {Text = emailDTO.Body}; using var smtp = new SmtpClient(); try { diff --git a/Services/RefreshTokenService.cs b/AuthenticationMicroservice/Services/RefreshTokenService.cs similarity index 100% rename from Services/RefreshTokenService.cs rename to AuthenticationMicroservice/Services/RefreshTokenService.cs diff --git a/Services/UserService.cs b/AuthenticationMicroservice/Services/UserService.cs similarity index 100% rename from Services/UserService.cs rename to AuthenticationMicroservice/Services/UserService.cs diff --git a/Settings/ConnectionStrings.cs b/AuthenticationMicroservice/Settings/ConnectionStrings.cs similarity index 71% rename from Settings/ConnectionStrings.cs rename to AuthenticationMicroservice/Settings/ConnectionStrings.cs index bc9e8b1e2d71c3b8d3304970cb4f1300f2feb022..0b08d8641bc80c3100c2b24ba18027a62ce1f5dd 100644 --- a/Settings/ConnectionStrings.cs +++ b/AuthenticationMicroservice/Settings/ConnectionStrings.cs @@ -3,5 +3,4 @@ public class ConnectionStrings { public string? DbConnectionString { get; set; } = ""; - public string? AzureBlobStorage { get; set; } = ""; } \ No newline at end of file diff --git a/Settings/EmailConfig.cs b/AuthenticationMicroservice/Settings/EmailConfig.cs similarity index 100% rename from Settings/EmailConfig.cs rename to AuthenticationMicroservice/Settings/EmailConfig.cs diff --git a/Settings/FrontendStrings.cs b/AuthenticationMicroservice/Settings/FrontendStrings.cs similarity index 100% rename from Settings/FrontendStrings.cs rename to AuthenticationMicroservice/Settings/FrontendStrings.cs diff --git a/Settings/TokenSettings.cs b/AuthenticationMicroservice/Settings/TokenSettings.cs similarity index 100% rename from Settings/TokenSettings.cs rename to AuthenticationMicroservice/Settings/TokenSettings.cs diff --git a/appsettings.json b/AuthenticationMicroservice/appsettings.json similarity index 95% rename from appsettings.json rename to AuthenticationMicroservice/appsettings.json index bc7d0aa4be83bf3cc276d7277220c8228ce6f327..99378402d6921fa624616b1c313528f1937d06c4 100644 --- a/appsettings.json +++ b/AuthenticationMicroservice/appsettings.json @@ -20,7 +20,7 @@ "Email": "", "Password": "" }, - "Group17Website": { + "FrontendStrings": { "BaseUrl": "" } } \ No newline at end of file diff --git a/azure-pipelines.yml b/AuthenticationMicroservice/azure-pipelines.yml similarity index 100% rename from azure-pipelines.yml rename to AuthenticationMicroservice/azure-pipelines.yml