Added forget/reset password feature to account module

pull/5105/head
liangshiwei 5 years ago
parent 478e93f9c3
commit 8614a45c8f

@ -7,5 +7,9 @@ namespace Volo.Abp.Account
public interface IAccountAppService : IApplicationService
{
Task<IdentityUserDto> RegisterAsync(RegisterDto input);
Task SendPasswordResetCodeAsync(SendPasswordResetCodeDto input);
Task ResetPasswordAsync(ResetPasswordDto input);
}
}
}

@ -44,6 +44,15 @@
"LoggedOutTitle": "Signed Out",
"LoggedOutText": "You have been signed out and you will be redirected soon.",
"ReturnToText": "Click here to redirect to {0}",
"OrLoginWith": "Or login with;"
"OrLoginWith": "Or login with;",
"ForgotPassword": "Forgot password?",
"SendPasswordResetLink_Information": "A password reset link will be sent to your email to reset your password. If you don't get an email within a few minutes, please re-try.",
"PasswordResetMailSentMessage": "Account recovery email sent to your e-mail address. If you don't see this email in your inbox within 15 minutes, look for it in your junk mail folder. If you find it there, please mark it as -Not Junk-. ",
"ResetPassword": "Reset Password",
"ConfirmPassword": "Confirm (repeat) the password",
"ResetPassword_Information": "Please enter your new password.",
"YourPasswordIsSuccessfullyReset": "Your password is successfully reset.",
"GoToTheApplication": "Go to the application",
"BackToLogin": "Back to login"
}
}

@ -40,6 +40,14 @@
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Je lastna registracija uporabnika omogočena",
"Description:Abp.Account.IsSelfRegistrationEnabled": "Ali lahko uporabnik sam registrira račun.",
"DisplayName:Abp.Account.EnableLocalLogin": "Avtenticirajte se z lokalnim računom",
"Description:Abp.Account.EnableLocalLogin": "Označuje, ali bo strežnik uporabnikom omogočil avtentikacijo z lokalnim računom."
"Description:Abp.Account.EnableLocalLogin": "Označuje, ali bo strežnik uporabnikom omogočil avtentikacijo z lokalnim računom.",
"ForgotPassword": "Ste pozabili geslo?",
"SendPasswordResetLink_Information": "Na vaš e-poštni naslov bo poslana povezava za ponastavitev gesla za ponastavitev gesla. Če v nekaj minutah ne dobite e-poštnega sporočila, poskusite znova.",
"PasswordResetMailSentMessage": "E-poštno sporočilo za obnovitev računa je bilo poslano na vaš e-poštni naslov. Če v 15 minutah tega e-poštnega sporočila ne vidite v mapi »Prejeto«, ga poiščite v mapi z neželeno pošto. Če ga najdete tam, ga prosimo označite kot -Ni neželeno-. ",
"ResetPassword": "Ponastavitev gesla",
"ConfirmPassword": "Potrditev (ponovitev) gesla",
"ResetPassword_Information": "Prosimo vnesite vaše novo geslo.",
"YourPasswordIsSuccessfullyReset": "Vaše geslo je uspešno ponastavljeno.",
"BackToLogin": "Nazaj na prijavo"
}
}

@ -43,6 +43,15 @@
"Description:Abp.Account.EnableLocalLogin": "Sunucunun, kullanıcıların yerel bir hesapla kimlik doğrulamasına izin verip vermeyeceğini belirtir.",
"LoggedOutTitle": ıkış Yaptınız",
"LoggedOutText": ıkış yaptınız ve birazdan yönlendirileceksiniz.",
"ReturnToText": "{0} uygulamasına dönmek için tıklayın."
"ReturnToText": "{0} uygulamasına dönmek için tıklayın.",
"ForgotPassword": "Şifremi unuttum",
"SendPasswordResetLink_Information": "E-posta adresinize bir şifre sıfırlama bağlantısı gönderilecektir. Birkaç dakika içerisinde bir e-posta almazsanız lütfen tekrar deneyin.",
"PasswordResetMailSentMessage": "E-posta adresinize bir şifre sıfırlama bağlantısı gönderilmiştir. Lütfen e-posta adresinizi kontrol ediniz. Eğer 15 dakika içinde, bu e-postayı gelen kutusunda bulamazsanız, gereksiz veya istenmeyen e-posta kutularına bakınız.",
"ResetPassword": "Şifre Yenileme",
"ConfirmPassword": "Şifre (tekrar)",
"ResetPassword_Information": "Lütfen yeni şifrenizi belirleyin.",
"YourPasswordIsSuccessfullyReset": "Şifreniz başarıyla sıfırlandı.",
"GoToTheApplication": "Uygulamaya git",
"BackToLogin": "Girişe dön"
}
}

@ -43,6 +43,15 @@
"Description:Abp.Account.EnableLocalLogin": "服务器是否将允许用户使用本地帐户进行身份验证。",
"LoggedOutTitle": "注销",
"LoggedOutText": "你已成功注销并将马上返回.",
"ReturnToText": "点击此处返回到 {0}"
"ReturnToText": "点击此处返回到 {0}",
"ForgotPassword": "忘记密码?",
"SendPasswordResetLink_Information": "密码重置链接将发送到您的电子邮件以重置密码. 如果您在几分钟内没有收到电子邮件,请重试.",
"PasswordResetMailSentMessage": "帐户恢复电子邮件已发送到您的电子邮件地址. 如果您在15分钟内未在收件箱中看到此电子邮件,请检查垃圾邮件,并标记为非垃圾邮件.",
"ResetPassword": "重设密码",
"ConfirmPassword": "确认(重复)密码",
"ResetPassword_Information": "请输入您的新密码.",
"YourPasswordIsSuccessfullyReset": "您的密码已经被重置成功.",
"GoToTheApplication": "转到应用程序",
"BackToLogin": "返回登录"
}
}

@ -0,0 +1,18 @@
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Auditing;
namespace Volo.Abp.Account
{
public class ResetPasswordDto
{
public Guid UserId { get; set; }
[Required]
public string ResetToken { get; set; }
[Required]
[DisableAuditing]
public string Password { get; set; }
}
}

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Identity;
using Volo.Abp.Validation;
namespace Volo.Abp.Account
{
public class SendPasswordResetCodeDto
{
[Required]
[EmailAddress]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))]
public string Email { get; set; }
[Required]
public string AppName { get; set; }
public string ReturnUrl { get; set; }
public string ReturnUrlHash { get; set; }
}
}

@ -11,11 +11,17 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Abp\Account\Emailing\Templates\*.tpl" />
<None Remove="Volo\Abp\Account\Emailing\Templates\*.tpl" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Text.Encodings.Web" Version="4.7.1" />
<ProjectReference Include="..\Volo.Abp.Account.Application.Contracts\Volo.Abp.Account.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\identity\src\Volo.Abp.Identity.Application\Volo.Abp.Identity.Application.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.UI.Navigation\Volo.Abp.UI.Navigation.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Emailing\Volo.Abp.Emailing.csproj" />
</ItemGroup>
<ItemGroup>

@ -1,7 +1,8 @@
using Volo.Abp.Identity;
using Volo.Abp.Emailing;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Account
@ -9,7 +10,8 @@ namespace Volo.Abp.Account
[DependsOn(
typeof(AbpAccountApplicationContractsModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpUiNavigationModule)
typeof(AbpUiNavigationModule),
typeof(AbpEmailingModule)
)]
public class AbpAccountApplicationModule : AbpModule
{
@ -19,6 +21,11 @@ namespace Volo.Abp.Account
{
options.FileSets.AddEmbedded<AbpAccountApplicationModule>();
});
Configure<AppUrlOptions>(options =>
{
options.Applications["MVC"].Urls[AccountUrlNames.PasswordReset] = "Account/ResetPassword";
});
}
}
}
}

@ -1,6 +1,7 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.Account.Emailing;
using Volo.Abp.Account.Settings;
using Volo.Abp.Application.Services;
using Volo.Abp.Identity;
@ -12,12 +13,18 @@ namespace Volo.Abp.Account
{
protected IIdentityRoleRepository RoleRepository { get; }
protected IdentityUserManager UserManager { get; }
protected IAccountEmailer AccountEmailer { get; }
protected IdentitySecurityLogManager IdentitySecurityLogManager { get; }
public AccountAppService(
IdentityUserManager userManager,
IIdentityRoleRepository roleRepository)
IIdentityRoleRepository roleRepository,
IAccountEmailer accountEmailer,
IdentitySecurityLogManager identitySecurityLogManager)
{
RoleRepository = roleRepository;
AccountEmailer = accountEmailer;
IdentitySecurityLogManager = identitySecurityLogManager;
UserManager = userManager;
}
@ -35,6 +42,37 @@ namespace Volo.Abp.Account
return ObjectMapper.Map<IdentityUser, IdentityUserDto>(user);
}
public virtual async Task SendPasswordResetCodeAsync(SendPasswordResetCodeDto input)
{
var user = await GetUserByEmail(input.Email);
var resetToken = await UserManager.GeneratePasswordResetTokenAsync(user);
await AccountEmailer.SendPasswordResetLinkAsync(user, resetToken, input.AppName, input.ReturnUrl, input.ReturnUrlHash);
}
public virtual async Task ResetPasswordAsync(ResetPasswordDto input)
{
var user = await UserManager.GetByIdAsync(input.UserId);
(await UserManager.ResetPasswordAsync(user, input.ResetToken, input.Password)).CheckErrors();
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
{
Identity = IdentitySecurityLogIdentityConsts.Identity,
Action = IdentitySecurityLogActionConsts.ChangePassword
});
}
protected virtual async Task<IdentityUser> GetUserByEmail(string email)
{
var user = await UserManager.FindByEmailAsync(email);
if (user == null)
{
throw new BusinessException("Volo.Account:InvalidEmailAddress")
.WithData("Email", email);
}
return user;
}
protected virtual async Task CheckSelfRegistrationAsync()
{
if (!await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled))
@ -43,4 +81,4 @@ namespace Volo.Abp.Account
}
}
}
}
}

@ -0,0 +1,7 @@
namespace Volo.Abp.Account
{
public static class AccountUrlNames
{
public const string PasswordReset = "Abp.Account.PasswordReset";
}
}

@ -0,0 +1,106 @@
using System;
using System.Diagnostics;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using System.Web;
using Microsoft.Extensions.Localization;
using Volo.Abp.Account.Emailing.Templates;
using Volo.Abp.Account.Localization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TextTemplating;
using Volo.Abp.UI.Navigation.Urls;
namespace Volo.Abp.Account.Emailing
{
public class AccountEmailer : IAccountEmailer, ITransientDependency
{
protected ITemplateRenderer TemplateRenderer { get; }
protected IEmailSender EmailSender { get; }
protected IStringLocalizer<AccountResource> StringLocalizer { get; }
protected IAppUrlProvider AppUrlProvider { get; }
protected ICurrentTenant CurrentTenant { get; }
public AccountEmailer(
IEmailSender emailSender,
ITemplateRenderer templateRenderer,
IStringLocalizer<AccountResource> stringLocalizer,
IAppUrlProvider appUrlProvider,
ICurrentTenant currentTenant)
{
EmailSender = emailSender;
StringLocalizer = stringLocalizer;
AppUrlProvider = appUrlProvider;
CurrentTenant = currentTenant;
TemplateRenderer = templateRenderer;
}
public virtual async Task SendPasswordResetLinkAsync(
IdentityUser user,
string resetToken,
string appName,
string returnUrl = null,
string returnUrlHash = null)
{
Debug.Assert(CurrentTenant.Id == user.TenantId, "This method can only work for current tenant!");
var url = await AppUrlProvider.GetResetPasswordUrlAsync(appName);
var link = $"{url}?userId={user.Id}&tenantId={user.TenantId}&resetToken={UrlEncoder.Default.Encode(resetToken)}";
if (!returnUrl.IsNullOrEmpty())
{
link += "&returnUrl=" + NormalizeReturnUrl(returnUrl);
}
if (!returnUrlHash.IsNullOrEmpty())
{
link += "&returnUrlHash=" + returnUrlHash;
}
var emailContent = await TemplateRenderer.RenderAsync(
AccountEmailTemplates.PasswordResetLink,
new { link = link }
);
await EmailSender.SendAsync(
user.Email,
StringLocalizer["PasswordReset"],
emailContent
);
}
private string NormalizeReturnUrl(string returnUrl)
{
if (returnUrl.IsNullOrEmpty())
{
return returnUrl;
}
//Handling openid connect login
if (returnUrl.StartsWith("/connect/authorize/callback", StringComparison.OrdinalIgnoreCase))
{
if (returnUrl.Contains("?"))
{
var queryPart = returnUrl.Split('?')[1];
var queryParameters = queryPart.Split('&');
foreach (var queryParameter in queryParameters)
{
if (queryParameter.Contains("="))
{
var queryParam = queryParameter.Split('=');
if (queryParam[0] == "redirect_uri")
{
return HttpUtility.UrlDecode(queryParam[1]);
}
}
}
}
}
return returnUrl;
}
}
}

@ -0,0 +1,13 @@
using System.Threading.Tasks;
using Volo.Abp.UI.Navigation.Urls;
namespace Volo.Abp.Account.Emailing
{
public static class AppUrlProviderAccountExtensions
{
public static Task<string> GetResetPasswordUrlAsync(this IAppUrlProvider appUrlProvider, string appName)
{
return appUrlProvider.GetUrlAsync(appName, AccountUrlNames.PasswordReset);
}
}
}

@ -0,0 +1,16 @@
using System.Threading.Tasks;
using Volo.Abp.Identity;
namespace Volo.Abp.Account.Emailing
{
public interface IAccountEmailer
{
Task SendPasswordResetLinkAsync(
IdentityUser user,
string resetToken,
string appName,
string returnUrl = null,
string returnUrlHash = null
);
}
}

@ -0,0 +1,22 @@
using Volo.Abp.Account.Localization;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.Localization;
using Volo.Abp.TextTemplating;
namespace Volo.Abp.Account.Emailing.Templates
{
public class AccountEmailTemplateDefinitionProvider : TemplateDefinitionProvider
{
public override void Define(ITemplateDefinitionContext context)
{
context.Add(
new TemplateDefinition(
AccountEmailTemplates.PasswordResetLink,
displayName: LocalizableString.Create<AccountResource>($"TextTemplate:{AccountEmailTemplates.PasswordResetLink}"),
layout: StandardEmailTemplates.Layout,
localizationResource: typeof(AccountResource)
).WithVirtualFilePath("/Volo/Abp/Account/Emailing/Templates/PasswordResetLink.tpl", true)
);
}
}
}

@ -0,0 +1,7 @@
namespace Volo.Abp.Account.Emailing.Templates
{
public static class AccountEmailTemplates
{
public const string PasswordResetLink = "Abp.Account.PasswordResetLink";
}
}

@ -0,0 +1,7 @@
<h3>{{L "PasswordReset"}}</h3>
<p>{{L "PasswordResetInfoInEmail"}}</p>
<div>
<a href="{{model.link}}">{{L "ResetMyPassword"}}</a>
</div>

@ -23,5 +23,19 @@ namespace Volo.Abp.Account
{
return AccountAppService.RegisterAsync(input);
}
[HttpPost]
[Route("send-password-reset-code")]
public virtual Task SendPasswordResetCodeAsync(SendPasswordResetCodeDto input)
{
return AccountAppService.SendPasswordResetCodeAsync(input);
}
[HttpPost]
[Route("reset-password")]
public virtual Task ResetPasswordAsync(ResetPasswordDto input)
{
return AccountAppService.ResetPasswordAsync(input);
}
}
}
}

@ -12,6 +12,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
{
public abstract class AccountPageModel : AbpPageModel
{
public IAccountAppService AccountAppService { get; set; }
public SignInManager<IdentityUser> SignInManager { get; set; }
public IdentityUserManager UserManager { get; set; }
public IdentitySecurityLogManager IdentitySecurityLogManager { get; set; }

@ -0,0 +1,21 @@
@page
@inject IHtmlLocalizer<AccountResource> L
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@model Volo.Abp.Account.Web.Pages.Account.ForgotPasswordModel
@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
@{
PageLayout.Content.Title = L["ForgotPassword"].Value;
}
<div class="account-module-form">
<form method="post">
<p>@L["SendPasswordResetLink_Information"]</p>
<input asp-for="ReturnUrl"/>
<input asp-for="ReturnUrlHash"/>
<abp-input asp-for="Email"/>
<abp-button button-type="Primary" size="Block" type="submit" class="mt-2 mb-3">@L["Submit"]</abp-button>
<a asp-page="./Login" asp-all-route-data="@(new Dictionary<string, string>{ {"returnUrl",Model.ReturnUrl}, {"returnUrlHash",Model.ReturnUrlHash} })">
<i class="fa fa-long-arrow-left"></i> @L["Login"]
</a>
</form>
</div>

@ -0,0 +1,51 @@
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Identity;
using Volo.Abp.Validation;
namespace Volo.Abp.Account.Web.Pages.Account
{
public class ForgotPasswordModel : AccountPageModel
{
[Required]
[EmailAddress]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))]
[BindProperty]
public string Email { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
public virtual Task<IActionResult> OnGetAsync()
{
return Task.FromResult<IActionResult>(Page());
}
public virtual async Task<IActionResult> OnPostAsync()
{
await AccountAppService.SendPasswordResetCodeAsync(
new SendPasswordResetCodeDto
{
Email = Email,
AppName = "MVC", //TODO: Const!
ReturnUrl = ReturnUrl,
ReturnUrlHash = ReturnUrlHash
}
);
return RedirectToPage(
"./PasswordResetLinkSent",
new
{
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash
});
}
}
}

@ -31,12 +31,14 @@
<input asp-for="LoginInput.Password" class="form-control"/>
<span asp-validation-for="LoginInput.Password" class="text-danger"></span>
</div>
<div class="form-check">
<label asp-for="LoginInput.RememberMe" class="form-check-label">
<input asp-for="LoginInput.RememberMe" class="form-check-input"/>
@Html.DisplayNameFor(m => m.LoginInput.RememberMe)
</label>
</div>
<abp-row>
<abp-column>
<abp-input asp-for="LoginInput.RememberMe" class="mb-4"/>
</abp-column>
<abp-column class="text-right">
<a href="@Url.Page("./ForgotPassword", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})">@L["ForgotPassword"]</a>
</abp-column>
</abp-row>
<abp-button type="submit" button-type="Primary" name="Action" value="Login" class="btn-block btn-lg mt-3">@L["Login"]</abp-button>
@if (Model.ShowCancelButton)
{

@ -0,0 +1,13 @@
@page
@model Volo.Abp.Account.Web.Pages.Account.PasswordResetLinkSentModel
@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@inject IHtmlLocalizer<AccountResource> L
@{
PageLayout.Content.Title = L["ForgotPassword"].Value;
}
<p>@L["PasswordResetMailSentMessage"]</p>
<div class="mt-4">
<a abp-button="Primary" asp-page="./Login" asp-all-route-data="@(new Dictionary<string, string>{ {"returnUrl",Model.ReturnUrl}, {"returnUrlHash",Model.ReturnUrlHash} })">&larr; @L["BackToLogin"]</a>
</div>

@ -0,0 +1,24 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Volo.Abp.Account.Web.Pages.Account
{
public class PasswordResetLinkSentModel : AccountPageModel
{
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
public virtual Task<IActionResult> OnGetAsync()
{
return Task.FromResult<IActionResult>(Page());
}
public virtual Task<IActionResult> OnPostAsync()
{
return Task.FromResult<IActionResult>(Page());
}
}
}

@ -0,0 +1,22 @@
@page
@inject IHtmlLocalizer<AccountResource> L
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@model Volo.Abp.Account.Web.Pages.Account.ResetPasswordModel
@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
@{
PageLayout.Content.Title = L["ResetPassword"].Value;
}
<form method="post">
<p>@L["ResetPassword_Information"]</p>
<abp-input asp-for="ReturnUrl" />
<abp-input asp-for="ReturnUrlHash" />
<abp-input asp-for="UserId" />
<abp-input asp-for="ResetToken" />
<abp-input asp-for="Password" />
<abp-input asp-for="ConfirmPassword" />
<a abp-button="Secondary" asp-page="./Login">@L["Cancel"]</a>
<abp-button type="submit" button-type="Primary" text="@L["Submit"].Value" />
</form>

@ -0,0 +1,110 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Auditing;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Validation;
namespace Volo.Abp.Account.Web.Pages.Account
{
//TODO: Implement live password complexity check on the razor view!
public class ResetPasswordModel : AccountPageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid? TenantId { get; set; }
[Required]
[HiddenInput]
[BindProperty(SupportsGet = true)]
public Guid UserId { get; set; }
[Required]
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ResetToken { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
[Required]
[BindProperty]
[DataType(DataType.Password)]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))]
[DisableAuditing]
public string Password { get; set; }
[Required]
[BindProperty]
[DataType(DataType.Password)]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))]
[DisableAuditing]
public string ConfirmPassword { get; set; }
protected virtual ITenantResolveResultAccessor TenantResolveResultAccessor { get; }
public ResetPasswordModel(ITenantResolveResultAccessor tenantResolveResultAccessor)
{
TenantResolveResultAccessor = tenantResolveResultAccessor;
}
public virtual Task<IActionResult> OnGetAsync()
{
//TODO: It would be good to try to switch tenant if needed
CheckCurrentTenant(TenantId);
return Task.FromResult<IActionResult>(Page());
}
public virtual async Task<IActionResult> OnPostAsync()
{
ValidateModel();
try
{
await AccountAppService.ResetPasswordAsync(
new ResetPasswordDto
{
UserId = UserId,
ResetToken = ResetToken,
Password = Password
}
);
}
catch (AbpIdentityResultException e)
{
if (!string.IsNullOrWhiteSpace(e.Message))
{
Alerts.Warning(e.Message);
return Page();
}
throw;
}
//TODO: Try to automatically login!
return RedirectToPage("./ResetPasswordConfirmation", new
{
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash
});
}
protected override void ValidateModel()
{
if (!Equals(Password, ConfirmPassword))
{
ModelState.AddModelError("ConfirmPassword", L["'{0}' and '{1}' do not match.", "ConfirmPassword", "Password"]);
}
base.ValidateModel();
}
}
}

@ -0,0 +1,11 @@
@page
@model Volo.Abp.Account.Web.Pages.Account.ResetPasswordConfirmationModel
@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@inject IHtmlLocalizer<AccountResource> L
@{
PageLayout.Content.Title = L["ResetPassword"].Value;
}
<p>@L["YourPasswordIsSuccessfullyReset"]</p>
<a abp-button="Primary" href="@Url.Content(Model.ReturnUrl)">@L["GoToTheApplication"]</a>

@ -0,0 +1,24 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Volo.Abp.Account.Web.Pages.Account
{
[AllowAnonymous]
public class ResetPasswordConfirmationModel : AccountPageModel
{
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get; set; }
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
public virtual Task<IActionResult> OnGetAsync()
{
ReturnUrl = GetRedirectUrl(ReturnUrl, ReturnUrlHash);
return Task.FromResult<IActionResult>(Page());
}
}
}

@ -1,5 +1,8 @@
using Volo.Abp.Account;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Account;
using Volo.Abp.AutoMapper;
using Volo.Abp.Emailing;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
@ -25,6 +28,10 @@ namespace MyCompanyName.MyProjectName
{
options.AddMaps<MyProjectNameApplicationModule>();
});
#if DEBUG
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
#endif
}
}
}

Loading…
Cancel
Save