Skip to content

Commit

Permalink
feat: implement password reset
Browse files Browse the repository at this point in the history
  • Loading branch information
CarlosPavajeau committed Nov 5, 2021
1 parent a2390b8 commit c164d87
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 10 deletions.
20 changes: 15 additions & 5 deletions src/Armory.Api/Controllers/ArmoryUsers/ArmoryUsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Armory.Api.Controllers.ArmoryUsers.Requests;
using Armory.Shared.Domain;
using Armory.Notifications.Application.SendResetPasswordEmail;
using Armory.Users.Application;
using Armory.Users.Application.ChangePassword;
using Armory.Users.Application.ConfirmEmail;
Expand Down Expand Up @@ -55,12 +55,22 @@ public async Task<IActionResult> ForgottenPassword(string userNameOrEmail)
{
var response = await _mediator.Send(
new GeneratePasswordResetTokenQuery(userNameOrEmail));
return response.TokenGenerated
? Ok(Utils.StringToBase64(response.Token))
: ArmoryUserNotFound(userNameOrEmail);
if (!response.TokenGenerated)
{
return ArmoryUserNotFound(userNameOrEmail);
}

await _mediator.Send(
new SendResetPasswordEmailCommand
{
Email = userNameOrEmail,
Token = response.Token
});

return Ok();
}

[HttpPost("[action]/{usernameOrEmail}")]
[HttpPut("[action]/{usernameOrEmail}")]
[AllowAnonymous]
public async Task<IActionResult> ResetPassword(string usernameOrEmail, [FromBody] ResetPasswordRequest request)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ namespace Armory.Api.Controllers.ArmoryUsers.Requests
{
public class ResetPasswordRequest
{
public string Email { get; set; }

[Required(ErrorMessage = "El token de reestablecimiento de contraseña es requerido.")]
public string Token { get; set; }

Expand Down
4 changes: 3 additions & 1 deletion src/Armory.Api/Extensions/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Armory.Api.Extensions.DependencyInjection.Squads;
using Armory.Api.Extensions.DependencyInjection.Troopers;
using Armory.Api.Extensions.DependencyInjection.Users;
using Armory.Notifications.Application.SendResetPasswordEmail;
using Microsoft.Extensions.DependencyInjection;

namespace Armory.Api.Extensions
Expand All @@ -25,7 +26,8 @@ public static IServiceCollection AddApplication(this IServiceCollection services
.AddPeopleApplication()
.AddRanksApplication()
.AddSquadsApplication()
.AddTroopersApplication();
.AddTroopersApplication()
.AddScoped<ResetPasswordEmailSender, ResetPasswordEmailSender>();

return services;
}
Expand Down
8 changes: 8 additions & 0 deletions src/Armory.Api/Extensions/Infrastructure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@
using Armory.Formats.WarMaterialAndSpecialEquipmentAssignmentFormats.Infrastructure.Persistence;
using Armory.Formats.WarMaterialDeliveryCertificateFormats.Domain;
using Armory.Formats.WarMaterialDeliveryCertificateFormats.Infrastructure.Persistence;
using Armory.Notifications.Domain;
using Armory.Notifications.Infrastructure;
using Armory.People.Domain;
using Armory.People.Infrastructure.Persistence;
using Armory.Ranks.Domain;
using Armory.Ranks.Infrastructure.Persistence;
using Armory.Shared.Domain;
using Armory.Shared.Domain.Bus.Event;
using Armory.Shared.Domain.ClosedXML;
using Armory.Shared.Domain.Persistence.EntityFramework;
Expand Down Expand Up @@ -87,6 +90,9 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi
services.AddAutoMapper(AssemblyHelper.GetInstance(Assemblies.Armory));

services.Configure<SecretKey>(configuration.GetSection("SecretKey"));
services.Configure<EmailSettings>(configuration.GetSection("EmailSettings"));
services.Configure<ApplicationProperties>(configuration.GetSection("ApplicationProperties"));

services.AddRouting(options => { options.LowercaseUrls = true; });

services.AddScoped<IArmoryUsersRepository, MySqlArmoryUsersRepository>();
Expand Down Expand Up @@ -115,6 +121,8 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi
services.AddScoped<IWorksheetManager, WorksheetManager>();
services.AddScoped<IEventBus, InMemoryEventBus>();

services.AddScoped<IEmailSender, EmailSender>();

return services;
}

Expand Down
10 changes: 10 additions & 0 deletions src/Armory.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
"SecretKey": {
"Key": "SECRET_AND_SECURE_KEY"
},
"EmailSettings": {
"Host": "smtp.gmail.com",
"Port": 587,
"Username": "",
"Password": "",
"EnableSsl": true
},
"ApplicationProperties": {
"FrontendUrl": "http://localhost:3000"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Threading.Tasks;
using Armory.Notifications.Domain;

namespace Armory.Notifications.Application.SendResetPasswordEmail
{
public class ResetPasswordEmailSender
{
private readonly IEmailSender _sender;

public ResetPasswordEmailSender(IEmailSender sender)
{
_sender = sender;
}

public async Task SendResetPasswordEmail(string email, string callbackUrl)
{
await _sender.SendResetPasswordEmail(email, callbackUrl);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Armory.Shared.Domain.Bus.Command;

namespace Armory.Notifications.Application.SendResetPasswordEmail
{
public class SendResetPasswordEmailCommand : Command
{
public string Email { get; init; }
public string Token { get; init; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Threading;
using System.Threading.Tasks;
using Armory.Shared.Domain;
using Armory.Shared.Domain.Bus.Command;
using Microsoft.Extensions.Options;

namespace Armory.Notifications.Application.SendResetPasswordEmail
{
public class SendResetPasswordEmailCommandHandler : CommandHandler<SendResetPasswordEmailCommand>
{
private readonly ApplicationProperties _properties;
private readonly ResetPasswordEmailSender _sender;

public SendResetPasswordEmailCommandHandler(ResetPasswordEmailSender sender,
IOptions<ApplicationProperties> properties)
{
_sender = sender;
_properties = properties.Value;
}

protected override async Task Handle(SendResetPasswordEmailCommand request, CancellationToken cancellationToken)
{
var callbackUrl =
$"{_properties.FrontendUrl}/reset_password?token={Utils.StringToBase64(request.Token)}&email={request.Email}";
await _sender.SendResetPasswordEmail(request.Email, callbackUrl);
}
}
}
11 changes: 11 additions & 0 deletions src/Armory/Notifications/Domain/EmailSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Armory.Notifications.Domain
{
public class EmailSettings
{
public string Host { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public int Port { get; set; }
public bool EnableSsl { get; set; }
}
}
18 changes: 18 additions & 0 deletions src/Armory/Notifications/Domain/IEmailSender.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Threading.Tasks;

namespace Armory.Notifications.Domain
{
/// <summary>
/// Interface for sending notifications via email
/// </summary>
public interface IEmailSender
{
/// <summary>
/// Send an email to reset the password
/// </summary>
/// <param name="email">Email to send</param>
/// <param name="callbackUrl">Callback url</param>
/// <returns></returns>
Task SendResetPasswordEmail(string email, string callbackUrl);
}
}
55 changes: 55 additions & 0 deletions src/Armory/Notifications/Infrastructure/EmailSender.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.Net;
using System.Net.Mail;
using System.Threading.Tasks;
using Armory.Notifications.Domain;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Armory.Notifications.Infrastructure
{
public class EmailSender : IEmailSender
{
private readonly SmtpClient _client;
private readonly ILogger<EmailSender> _logger;
private readonly EmailSettings _settings;

public EmailSender(IOptions<EmailSettings> settings, ILogger<EmailSender> logger)
{
_settings = settings.Value;
_logger = logger;

_client = new SmtpClient(_settings.Host, _settings.Port)
{
Credentials = new NetworkCredential(_settings.Username, _settings.Password),
EnableSsl = _settings.EnableSsl
};
}

public async Task SendResetPasswordEmail(string email, string callbackUrl)
{
var message = new MailMessage(_settings.Username, email)
{
Subject = "Reestrablecer contraseña",
Body =
$"Por favor, restablezca su contraseña haciendo clic <a href='{callbackUrl}' target='blank'>aquí</a>.",
IsBodyHtml = true,
Priority = MailPriority.High
};

await SendEmail(message);
}

private async Task SendEmail(MailMessage message)
{
try
{
await _client.SendMailAsync(message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending email");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ namespace Armory.Users.Application.ResetPassword
{
public class ResetPasswordCommand : Command
{
public ResetPasswordCommand(string usernameOrEmail, string token, string newPassword)
public ResetPasswordCommand(string email, string token, string newPassword)
{
UsernameOrEmail = usernameOrEmail;
Email = email;
Token = token;
NewPassword = newPassword;
}

public string UsernameOrEmail { get; }
public string Email { get; }
public string Token { get; }
public string NewPassword { get; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public ResetPasswordCommandHandler(PasswordRestorer restorer)

protected override async Task Handle(ResetPasswordCommand request, CancellationToken cancellationToken)
{
await _restorer.ResetPassword(request.UsernameOrEmail, request.Token, request.NewPassword);
await _restorer.ResetPassword(request.Email, request.Token, request.NewPassword);
}
}
}
7 changes: 7 additions & 0 deletions src/Shared/Domain/ApplicationProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Armory.Shared.Domain
{
public class ApplicationProperties
{
public string FrontendUrl { get; set; }
}
}

0 comments on commit c164d87

Please sign in to comment.