mirror of
https://github.com/bitwarden/server.git
synced 2025-05-22 20:11:04 -05:00
account recovery to delete via email
This commit is contained in:
parent
503370d059
commit
b2295f867b
@ -345,6 +345,38 @@ namespace Bit.Api.Controllers
|
|||||||
throw new BadRequestException(ModelState);
|
throw new BadRequestException(ModelState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpPost("delete-recover")]
|
||||||
|
public async Task PostDeleteRecover([FromBody]DeleteRecoverRequestModel model)
|
||||||
|
{
|
||||||
|
await _userService.SendDeleteConfirmationAsync(model.Email);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("delete-recover-token")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task PostDeleteRecoverToken([FromBody]VerifyDeleteRecoverRequestModel model)
|
||||||
|
{
|
||||||
|
var user = await _userService.GetUserByIdAsync(new Guid(model.UserId));
|
||||||
|
if(user == null)
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _userService.DeleteAsync(user, model.Token);
|
||||||
|
if(result.Succeeded)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var error in result.Errors)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(2000);
|
||||||
|
throw new BadRequestException(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("premium")]
|
[HttpPost("premium")]
|
||||||
public async Task<ProfileResponseModel> PostPremium([FromBody]PremiumRequestModel model)
|
public async Task<ProfileResponseModel> PostPremium([FromBody]PremiumRequestModel model)
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="MailTemplates\VerifyDelete.cshtml" />
|
||||||
|
<EmbeddedResource Include="MailTemplates\VerifyDelete.text.cshtml" />
|
||||||
<EmbeddedResource Include="MailTemplates\VerifyEmail.cshtml" />
|
<EmbeddedResource Include="MailTemplates\VerifyEmail.cshtml" />
|
||||||
<EmbeddedResource Include="MailTemplates\VerifyEmail.text.cshtml" />
|
<EmbeddedResource Include="MailTemplates\VerifyEmail.text.cshtml" />
|
||||||
<EmbeddedResource Include="MailTemplates\TwoFactorEmail.cshtml" />
|
<EmbeddedResource Include="MailTemplates\TwoFactorEmail.cshtml" />
|
||||||
|
9
src/Core/MailTemplates/VerifyDelete.cshtml
Normal file
9
src/Core/MailTemplates/VerifyDelete.cshtml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@model Bit.Core.Models.Mail.VerifyDeleteModel
|
||||||
|
@{
|
||||||
|
Layout = "_BasicMailLayout";
|
||||||
|
}
|
||||||
|
<p>
|
||||||
|
Click the link below to delete your bitwarden account (@Model.Email).
|
||||||
|
If you did not request this email to delete your bitwarden account, you can safely ignore it.
|
||||||
|
</p>
|
||||||
|
<p><a href="@Model.Url" target="_blank" clicktracking=off>@Model.Url</a></p>
|
10
src/Core/MailTemplates/VerifyDelete.text.cshtml
Normal file
10
src/Core/MailTemplates/VerifyDelete.text.cshtml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
@model Bit.Core.Models.Mail.VerifyDeleteModel
|
||||||
|
@{
|
||||||
|
Layout = "_BasicMailLayout.text";
|
||||||
|
}
|
||||||
|
Click the link below to delete your bitwarden
|
||||||
|
account (@Model.Email). If you did not request
|
||||||
|
this email to delete your bitwarden account,
|
||||||
|
you can safely ignore it.
|
||||||
|
|
||||||
|
@Model.Url
|
@ -0,0 +1,12 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Api
|
||||||
|
{
|
||||||
|
public class DeleteRecoverRequestModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
[StringLength(50)]
|
||||||
|
public string Email { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Api
|
||||||
|
{
|
||||||
|
public class VerifyDeleteRecoverRequestModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string UserId { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Token { get; set; }
|
||||||
|
}
|
||||||
|
}
|
18
src/Core/Models/Mail/VerifyDeleteModel.cs
Normal file
18
src/Core/Models/Mail/VerifyDeleteModel.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Mail
|
||||||
|
{
|
||||||
|
public class VerifyDeleteModel : BaseMailModel
|
||||||
|
{
|
||||||
|
public string Url => string.Format("{0}/verify-recover-delete?userId={1}&token={2}&email={3}",
|
||||||
|
WebVaultUrl,
|
||||||
|
UserId,
|
||||||
|
Token,
|
||||||
|
EmailEncoded);
|
||||||
|
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string EmailEncoded { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
Task SendWelcomeEmailAsync(User user);
|
Task SendWelcomeEmailAsync(User user);
|
||||||
Task SendVerifyEmailEmailAsync(string email, Guid userId, string token);
|
Task SendVerifyEmailEmailAsync(string email, Guid userId, string token);
|
||||||
|
Task SendVerifyDeleteEmailAsync(string email, Guid userId, string token);
|
||||||
Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail);
|
Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail);
|
||||||
Task SendChangeEmailEmailAsync(string newEmailAddress, string token);
|
Task SendChangeEmailEmailAsync(string newEmailAddress, string token);
|
||||||
Task SendTwoFactorEmailAsync(string email, string token);
|
Task SendTwoFactorEmailAsync(string email, string token);
|
||||||
|
@ -38,6 +38,8 @@ namespace Bit.Core.Services
|
|||||||
Task<bool> RecoverTwoFactorAsync(string email, string masterPassword, string recoveryCode);
|
Task<bool> RecoverTwoFactorAsync(string email, string masterPassword, string recoveryCode);
|
||||||
Task<string> GenerateUserTokenAsync(User user, string tokenProvider, string purpose);
|
Task<string> GenerateUserTokenAsync(User user, string tokenProvider, string purpose);
|
||||||
Task<IdentityResult> DeleteAsync(User user);
|
Task<IdentityResult> DeleteAsync(User user);
|
||||||
|
Task<IdentityResult> DeleteAsync(User user, string token);
|
||||||
|
Task SendDeleteConfirmationAsync(string email);
|
||||||
Task SignUpPremiumAsync(User user, string paymentToken, short additionalStorageGb);
|
Task SignUpPremiumAsync(User user, string paymentToken, short additionalStorageGb);
|
||||||
Task AdjustStorageAsync(User user, short storageAdjustmentGb);
|
Task AdjustStorageAsync(User user, short storageAdjustmentGb);
|
||||||
Task ReplacePaymentMethodAsync(User user, string paymentToken);
|
Task ReplacePaymentMethodAsync(User user, string paymentToken);
|
||||||
|
@ -47,6 +47,25 @@ namespace Bit.Core.Services
|
|||||||
await _mailDeliveryService.SendEmailAsync(message);
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SendVerifyDeleteEmailAsync(string email, Guid userId, string token)
|
||||||
|
{
|
||||||
|
var message = CreateDefaultMessage("Delete Your Account", email);
|
||||||
|
var model = new VerifyDeleteModel
|
||||||
|
{
|
||||||
|
Token = WebUtility.UrlEncode(token),
|
||||||
|
UserId = userId,
|
||||||
|
WebVaultUrl = _globalSettings.BaseServiceUri.Vault,
|
||||||
|
SiteName = _globalSettings.SiteName,
|
||||||
|
Email = email,
|
||||||
|
EmailEncoded = WebUtility.UrlEncode(email)
|
||||||
|
};
|
||||||
|
message.HtmlContent = _engine.Parse("VerifyDelete", model);
|
||||||
|
message.TextContent = _engine.Parse("VerifyDelete.text", model);
|
||||||
|
message.MetaData.Add("SendGridBypassListManagement", true);
|
||||||
|
|
||||||
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail)
|
public async Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail)
|
||||||
{
|
{
|
||||||
var message = CreateDefaultMessage("Your Email Change", toEmail);
|
var message = CreateDefaultMessage("Your Email Change", toEmail);
|
||||||
|
@ -11,6 +11,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
private const string WelcomeTemplateId = "045f8ad5-5547-4fa2-8d3d-6d46e401164d";
|
private const string WelcomeTemplateId = "045f8ad5-5547-4fa2-8d3d-6d46e401164d";
|
||||||
private const string VerifyEmailTemplateId = "TODO";
|
private const string VerifyEmailTemplateId = "TODO";
|
||||||
|
private const string VerifyDeleteTemplateId = "TODO";
|
||||||
private const string ChangeEmailAlreadyExistsTemplateId = "b69d2038-6ad9-4cf6-8f7f-7880921cba43";
|
private const string ChangeEmailAlreadyExistsTemplateId = "b69d2038-6ad9-4cf6-8f7f-7880921cba43";
|
||||||
private const string ChangeEmailTemplateId = "ec2c1471-8292-4f17-b6b6-8223d514f86e";
|
private const string ChangeEmailTemplateId = "ec2c1471-8292-4f17-b6b6-8223d514f86e";
|
||||||
private const string TwoFactorEmailTemplateId = "264cfe69-5258-4c89-8d90-76b4659de589";
|
private const string TwoFactorEmailTemplateId = "264cfe69-5258-4c89-8d90-76b4659de589";
|
||||||
@ -53,7 +54,7 @@ namespace Bit.Core.Services
|
|||||||
email,
|
email,
|
||||||
VerifyEmailTemplateId);
|
VerifyEmailTemplateId);
|
||||||
|
|
||||||
AddSubstitution(message, "{{token}}", Uri.EscapeDataString(token));
|
AddSubstitution(message, "{{token}}", WebUtility.UrlEncode(token));
|
||||||
AddSubstitution(message, "{{userId}}", userId.ToString());
|
AddSubstitution(message, "{{userId}}", userId.ToString());
|
||||||
AddCategories(message, new List<string> { AdministrativeCategoryName, "Verify Email" });
|
AddCategories(message, new List<string> { AdministrativeCategoryName, "Verify Email" });
|
||||||
message.MetaData.Add("SendGridBypassListManagement", true);
|
message.MetaData.Add("SendGridBypassListManagement", true);
|
||||||
@ -61,6 +62,23 @@ namespace Bit.Core.Services
|
|||||||
await _mailDeliveryService.SendEmailAsync(message);
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task SendVerifyDeleteEmailAsync(string email, Guid userId, string token)
|
||||||
|
{
|
||||||
|
var message = CreateDefaultMessage(
|
||||||
|
"Delete Your Account",
|
||||||
|
email,
|
||||||
|
VerifyDeleteTemplateId);
|
||||||
|
|
||||||
|
AddSubstitution(message, "{{token}}", WebUtility.UrlEncode(token));
|
||||||
|
AddSubstitution(message, "{{email}}", email);
|
||||||
|
AddSubstitution(message, "{{emailUrlEncoded}}", WebUtility.UrlEncode(email));
|
||||||
|
AddSubstitution(message, "{{userId}}", userId.ToString());
|
||||||
|
AddCategories(message, new List<string> { AdministrativeCategoryName, "Verify Delete" });
|
||||||
|
message.MetaData.Add("SendGridBypassListManagement", true);
|
||||||
|
|
||||||
|
await _mailDeliveryService.SendEmailAsync(message);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail)
|
public async Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail)
|
||||||
{
|
{
|
||||||
var message = CreateDefaultMessage(
|
var message = CreateDefaultMessage(
|
||||||
|
@ -178,6 +178,29 @@ namespace Bit.Core.Services
|
|||||||
return IdentityResult.Success;
|
return IdentityResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IdentityResult> DeleteAsync(User user, string token)
|
||||||
|
{
|
||||||
|
if(!(await VerifyUserTokenAsync(user, TokenOptions.DefaultProvider, "DeleteAccount", token)))
|
||||||
|
{
|
||||||
|
return IdentityResult.Failed(ErrorDescriber.InvalidToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
return await DeleteAsync(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendDeleteConfirmationAsync(string email)
|
||||||
|
{
|
||||||
|
var user = await _userRepository.GetByEmailAsync(email);
|
||||||
|
if(user == null)
|
||||||
|
{
|
||||||
|
// No user exists.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var token = await base.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, "DeleteAccount");
|
||||||
|
await _mailService.SendVerifyDeleteEmailAsync(user.Email, user.Id, token);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<IdentityResult> RegisterUserAsync(User user, string masterPassword)
|
public async Task<IdentityResult> RegisterUserAsync(User user, string masterPassword)
|
||||||
{
|
{
|
||||||
var result = await base.CreateAsync(user, masterPassword);
|
var result = await base.CreateAsync(user, masterPassword);
|
||||||
|
@ -56,5 +56,10 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task SendVerifyDeleteEmailAsync(string email, Guid userId, string token)
|
||||||
|
{
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user