password reset

This commit is contained in:
Steven Hildreth 2019-01-08 09:51:26 -06:00
parent 74d0aa591e
commit b09aa752a6
6 changed files with 148 additions and 8 deletions

View file

@ -0,0 +1,64 @@
using Mapster;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Roadie.Library;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Encoding;
using Roadie.Library.Enums;
using Roadie.Library.Extensions;
using Roadie.Library.Models;
using Roadie.Library.Models.Collections;
using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Statistics;
using Roadie.Library.Models.Users;
using Roadie.Library.Utility;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Net;
using System.Net.Mail;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using data = Roadie.Library.Data;
namespace Roadie.Api.Services
{
public class EmailSenderService : IEmailSender
{
protected IRoadieSettings Configuration { get;}
public EmailSenderService(IRoadieSettings configuration)
{
this.Configuration = configuration;
}
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
{
using (MailMessage mail = new MailMessage(this.Configuration.SmtpFromAddress, email))
{
using (SmtpClient client = new SmtpClient())
{
client.Port = this.Configuration.SmtpPort;
client.EnableSsl = this.Configuration.SmtpUseSSl;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(this.Configuration.SmtpUsername, this.Configuration.SmtpPassword);
client.Host = this.Configuration.SmtpHost;
mail.Subject = subject;
mail.IsBodyHtml = true;
mail.Body = htmlMessage;
ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
};
await client.SendMailAsync(mail);
}
}
}
}
}

View file

@ -17,4 +17,10 @@
<ProjectReference Include="..\Roadie.Api.Library\Roadie.Api.Library.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Identity.UI">
<HintPath>..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.identity.ui\2.2.0\lib\netstandard2.0\Microsoft.AspNetCore.Identity.UI.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View file

@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
@ -12,6 +14,7 @@ using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
namespace Roadie.Api.Controllers
@ -30,6 +33,7 @@ namespace Roadie.Api.Controllers
private IRoadieSettings RoadieSettings { get; }
private ICacheManager CacheManager { get; }
private IAdminService AdminService { get; }
private IEmailSender EmailSender { get; }
public AccountController(
IAdminService adminService,
@ -38,7 +42,8 @@ namespace Roadie.Api.Controllers
IConfiguration configuration,
ILogger<AccountController> logger,
ITokenService tokenService,
ICacheManager cacheManager)
ICacheManager cacheManager,
IEmailSender emailSender)
{
this.UserManager = userManager;
this.SignInManager = signInManager;
@ -50,6 +55,7 @@ namespace Roadie.Api.Controllers
this.RoadieSettings = new RoadieSettings();
configuration.GetSection("RoadieSettings").Bind(this.RoadieSettings);
this.AdminService = adminService;
this.EmailSender = emailSender;
}
[HttpPost]
@ -74,6 +80,12 @@ namespace Roadie.Api.Controllers
await UserManager.UpdateAsync(user);
var t = await this.TokenService.GenerateToken(user, this.UserManager);
this.Logger.LogInformation($"Successfully authenticated User [{ model.Username}]");
if(!user.EmailConfirmed)
{
var code = await this.UserManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, Request.Scheme);
await this.EmailSender.SendEmailAsync(user.Email, $"Confirm your { this.RoadieSettings.SiteName } email", $"Please confirm your { this.RoadieSettings.SiteName } account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
}
this.CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
var avatarUrl = $"{this.Request.Scheme}://{this.Request.Host}/images/user/{ user.RoadieId }/{ this.RoadieSettings.ThumbnailImageSize.Width }/{ this.RoadieSettings.ThumbnailImageSize.Height }";
return Ok(new
@ -138,6 +150,10 @@ namespace Roadie.Api.Controllers
{
await this.AdminService.DoInitialSetup(user, this.UserManager);
}
var code = await this.UserManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, Request.Scheme);
await this.EmailSender.SendEmailAsync(user.Email, $"Confirm your { this.RoadieSettings.SiteName } email", $"Please confirm your { this.RoadieSettings.SiteName } account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
await SignInManager.SignInAsync(user, isPersistent: false);
var t = await this.TokenService.GenerateToken(user, this.UserManager);
this.Logger.LogInformation($"Successfully created and authenticated User [{ registerModel.Username}]");
@ -162,7 +178,34 @@ namespace Roadie.Api.Controllers
return BadRequest(ModelState);
}
[HttpPost]
[HttpGet("confirmemail")]
public IActionResult ConfirmEmail(string userid, string code)
{
var user = this.UserManager.FindByIdAsync(userid).Result;
IdentityResult result = this.UserManager.ConfirmEmailAsync(user, code).Result;
if (result.Succeeded)
{
this.Logger.LogInformation("User [{0}] Confirmed Email Successfully", userid);
return Content($"Email for { this.RoadieSettings.SiteName } account confirmed successfully!");
}
else
{
return Content("Error while confirming your email!");
}
}
[HttpGet("sendpasswordresetemail")]
public async Task<IActionResult> SendPasswordResetEmail(string username, string callbackUrl)
{
var user = await UserManager.FindByNameAsync(username);
var token = await this.UserManager.GeneratePasswordResetTokenAsync(user);
callbackUrl = callbackUrl + "?username=" + username + "&token=" + token;
await this.EmailSender.SendEmailAsync(user.Email, $"Reset your { this.RoadieSettings.SiteName } password", $"A request has been made to reset your password for your { this.RoadieSettings.SiteName } account. To proceed <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>click here</a>.");
this.Logger.LogInformation("User [{0}] Email [{1}] Requested Password Reset Callback [{2}]", username, user.Email, callbackUrl);
return Ok();
}
[HttpPost("resetpassword")]
public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordModel resetPasswordModel)
{
if (ModelState.IsValid)
@ -179,7 +222,18 @@ namespace Roadie.Api.Controllers
{
this.CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
await SignInManager.SignInAsync(user, isPersistent: false);
return Ok(this.TokenService.GenerateToken(user, this.UserManager));
var avatarUrl = $"{this.Request.Scheme}://{this.Request.Host}/images/user/{ user.RoadieId }/{ this.RoadieSettings.ThumbnailImageSize.Width }/{ this.RoadieSettings.ThumbnailImageSize.Height }";
var t = await this.TokenService.GenerateToken(user, this.UserManager);
return Ok(new
{
Username = user.UserName,
user.Email,
user.LastLogin,
avatarUrl,
Token = t,
user.Timeformat,
user.Timezone
});
}
else
{

View file

@ -13,10 +13,11 @@ by editing this MSBuild file. In order to learn more about this please visit htt
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<ProjectGuid>68c80416-0d72-409d-b727-3fea7ab7fd2c</ProjectGuid>
<publishUrl>bin\x64\Debug\netcoreapp2.1\publish\</publishUrl>
<publishUrl>bin\x64\Debug\netcoreapp2.2\publish\</publishUrl>
<DeleteExistingFiles>False</DeleteExistingFiles>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<SelfContained>false</SelfContained>
<_IsPortable>true</_IsPortable>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup>
</Project>

View file

@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
@ -23,6 +24,7 @@ using Roadie.Library.Identity;
using Roadie.Library.Imaging;
using Roadie.Library.Utility;
using System;
using System.Diagnostics;
namespace Roadie.Api
{
@ -88,8 +90,8 @@ namespace Roadie.Api
}));
services.AddSingleton<ITokenService, TokenService>();
services.AddSingleton<IHttpEncoder, HttpEncoder>();
services.AddSingleton<IEmailSender, EmailSenderService>();
var cacheManager = new DictionaryCacheManager(this._loggerFactory.CreateLogger<DictionaryCacheManager>(), new CachePolicy(TimeSpan.FromHours(4)));
services.AddSingleton<ICacheManager>(cacheManager);
@ -112,7 +114,8 @@ namespace Roadie.Api
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<ApplicationUserDbContext>();
.AddEntityFrameworkStores<ApplicationUserDbContext>()
.AddDefaultTokenProviders();
services.AddAuthorization(options =>
{
@ -134,7 +137,13 @@ namespace Roadie.Api
var integrationKeys = this._configuration.GetSection("IntegrationKeys")
.Get<IntegrationKey>();
settings.Integrations.ApiKeys = new System.Collections.Generic.List<ApiKey>
if(integrationKeys == null)
{
Console.WriteLine("Unable to find IntegrationKeys, Integrations will not have proper API keys setup.");
}
else if (integrationKeys != null)
{
settings.Integrations.ApiKeys = new System.Collections.Generic.List<ApiKey>
{
new ApiKey
{
@ -154,6 +163,7 @@ namespace Roadie.Api
Key = integrationKeys.BingImageSearch
}
};
}
return settings;
});
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

View file

@ -82,6 +82,11 @@
"OGGConvertCommand": "ffmpeg -i \"{0}\" -acodec libmp3lame -q:a 0 \"{1}\"\"",
"APEConvertCommand": "ffmpeg -i \"{0}\" \"{1}\""
},
"SmtpFromAddress": "roadie@hildreth.email",
"SmtpPort": 587,
"SmtpUsername": "roadie@hildreth.email",
"SmtpHost": "box.hildreth.email",
"SmtpUseSSl": true,
"Integrations": {
"ITunesProviderEnabled": true,
"MusicBrainzProviderEnabled": true,