-
Notifications
You must be signed in to change notification settings - Fork 645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Account delete user flow #4977
Account delete user flow #4977
Changes from 1 commit
e909727
3d30a49
f635792
bc6530a
6756442
27243fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,24 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
using System.ComponentModel.DataAnnotations; | ||
|
||
namespace NuGetGallery.Areas.Admin.ViewModels | ||
{ | ||
public class DeleteUserAccountViewModel | ||
public class DeleteUserAccountViewModel : DeleteAccountViewModel | ||
{ | ||
public DeleteUserAccountViewModel() | ||
{ | ||
ShouldUnlist = true; | ||
} | ||
|
||
public List<ListPackageItemViewModel> Packages { get; set; } | ||
|
||
public User User { get; set; } | ||
|
||
public string AccountName { get; set; } | ||
|
||
[Required(ErrorMessage = "Please sign using your name.")] | ||
[StringLength(1000)] | ||
[Display(Name = "Signature")] | ||
public string Signature { get; set; } | ||
|
||
[Display(Name = "Unlist the packages with no other owners.")] | ||
public bool ShouldUnlist { get; set; } | ||
|
||
public bool HasOrphanPackages { get; set; } | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -775,6 +775,7 @@ public virtual ActionResult ContactOwners(string id) | |
CopySender = true, | ||
}; | ||
|
||
ViewData["HasOwners"] = package.PackageRegistration.Owners.Count > 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
return View(model); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
using System.Net.Mail; | ||
using System.Threading.Tasks; | ||
using System.Web.Mvc; | ||
using NuGetGallery.Areas.Admin; | ||
using NuGetGallery.Areas.Admin.ViewModels; | ||
using NuGetGallery.Authentication; | ||
using NuGetGallery.Configuration; | ||
|
@@ -28,6 +29,7 @@ public partial class UsersController | |
private readonly AuthenticationService _authService; | ||
private readonly ICredentialBuilder _credentialBuilder; | ||
private readonly IDeleteAccountService _deleteAccountService; | ||
private readonly ISupportRequestService _supportRequestService; | ||
|
||
public UsersController( | ||
ICuratedFeedService feedsQuery, | ||
|
@@ -38,7 +40,8 @@ public UsersController( | |
IAppConfiguration config, | ||
AuthenticationService authService, | ||
ICredentialBuilder credentialBuilder, | ||
IDeleteAccountService deleteAccountService) | ||
IDeleteAccountService deleteAccountService, | ||
ISupportRequestService supportRequestService) | ||
{ | ||
_curatedFeedService = feedsQuery ?? throw new ArgumentNullException(nameof(feedsQuery)); | ||
_userService = userService ?? throw new ArgumentNullException(nameof(userService)); | ||
|
@@ -49,6 +52,7 @@ public UsersController( | |
_authService = authService ?? throw new ArgumentNullException(nameof(authService)); | ||
_credentialBuilder = credentialBuilder ?? throw new ArgumentNullException(nameof(credentialBuilder)); | ||
_deleteAccountService = deleteAccountService ?? throw new ArgumentNullException(nameof(deleteAccountService)); | ||
_supportRequestService = supportRequestService ?? throw new ArgumentNullException(nameof(supportRequestService)); | ||
} | ||
|
||
[HttpGet] | ||
|
@@ -94,6 +98,59 @@ public virtual ActionResult Account() | |
return AccountView(new AccountViewModel()); | ||
} | ||
|
||
[HttpGet] | ||
[Authorize] | ||
public virtual ActionResult DeleteRequest() | ||
{ | ||
var user = GetCurrentUser(); | ||
|
||
if (user == null || user.IsDeleted) | ||
{ | ||
return HttpNotFound("User not found."); | ||
} | ||
bool pendingRequest = _supportRequestService.GetIssues(assignedTo: null, reason: null, issueStatusId: null, galleryUsername: user.Username) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If someone cancels a request to delete their account, won't this still find the existing request? It seems a bit awkward to query the support request service for whether or not a user requested deletion. Additionally, when we move off our support request system, this will need to be changed. I would rather store this in the database somehow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the first question - no they will not find it because it was not created. |
||
.Any(issue => string.Equals(issue.IssueTitle, Strings.AccountDelete_SupportRequestTitle, StringComparison.CurrentCultureIgnoreCase) && | ||
issue.IssueStatus.Key != 3 && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code was updated. It represents the status for a specific issue. ( 3 == resolved) |
||
issue.OwnerEmail == user.EmailAddress); | ||
ViewData["HasPendingRequest"] = pendingRequest; | ||
|
||
var listPackageItems = _packageService | ||
.FindPackagesByOwner(user, includeUnlisted: true) | ||
.Select(p => new ListPackageItemViewModel(p)) | ||
.ToList(); | ||
|
||
var model = new DeleteAccountViewModel() | ||
{ | ||
Packages = listPackageItems, | ||
User = user, | ||
AccountName = user.Username, | ||
HasOrphanPackages = listPackageItems.Any(p => p.Owners.Count <= 1), | ||
}; | ||
|
||
return View("DeleteAccount", model); | ||
} | ||
|
||
[HttpPost] | ||
[Authorize] | ||
[ValidateAntiForgeryToken] | ||
public virtual async Task<ActionResult> SendAccountDeleteNotificationAsync() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RequestAccountDeletionAsync? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The async suffix was added to follow the naming convention for the async methods. The observation that @scottbommarito made is correct. The name was changed to RequestAccountDeletion to be in sync with the current pattern. |
||
{ | ||
var user = GetCurrentUser(); | ||
bool createSupportRequestStatus = await _supportRequestService.AddNewSupportRequestAsync(Strings.AccountDelete_SupportRequestTitle, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are no checks here: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated. |
||
Strings.AccountDelete_SupportRequestTitle, | ||
user.EmailAddress, | ||
"The user requested to have the account deleted.", | ||
user); | ||
if (!createSupportRequestStatus) | ||
{ | ||
TempData["RequestFailedMessage"] = Strings.AccountDelete_CreateSupportRequestFails; | ||
return RedirectToAction("DeleteRequest"); | ||
} | ||
_messageService.SendAccountDeleteNotice(user.ToMailAddress(), user.Username); | ||
|
||
return RedirectToAction("DeleteRequest"); | ||
} | ||
|
||
[HttpGet] | ||
[Authorize(Roles = "Admins")] | ||
public virtual ActionResult Delete(string accountName) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -647,6 +647,32 @@ [change your email notification settings]({5}). | |
} | ||
} | ||
|
||
public void SendAccountDeleteNotice(MailAddress mailAddress, string account) | ||
{ | ||
string body = @"We received a request to delete your account {0}. If you did not initiate this request please contact the {1} team. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The formatting here could be much better. Also there's a couple oddly worded phrases in here. You should also make similar modifications to the page itself.
For comparison, it is currently:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
When your account will be deleted, we will:revoke your API key(s), | ||
remove you as the owner for any package you own, dissociate all previously existing ID prefix reservation with this account. If you were the only account tied to a particular ID prefix reservation the prefix will be available to anyone. | ||
We will not delete the NuGet packages associated with the account. | ||
|
||
Thanks, | ||
The {1} Team"; | ||
|
||
body = String.Format( | ||
CultureInfo.CurrentCulture, | ||
body, | ||
account, | ||
Config.GalleryOwner.DisplayName); | ||
|
||
using (var mailMessage = new MailMessage()) | ||
{ | ||
mailMessage.Subject = Strings.AccountDelete_SupportRequestTitle; | ||
mailMessage.Body = body; | ||
mailMessage.From = Config.GalleryNoReplyAddress; | ||
|
||
mailMessage.To.Add(mailAddress.Address); | ||
SendMessage(mailMessage); | ||
} | ||
} | ||
private static void AddOwnersToMailMessage(PackageRegistration packageRegistration, MailMessage mailMessage) | ||
{ | ||
foreach (var owner in packageRegistration.Owners.Where(o => o.EmailAllowed)) | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -618,4 +618,13 @@ For more information, please contact '{2}'.</value> | |
<data name="AccountDelete_Success" xml:space="preserve"> | ||
<value>The account:{0} was deleted succesfully.</value> | ||
</data> | ||
<data name="AccountDelete_CreateSupportRequestFails" xml:space="preserve"> | ||
<value>The request failed to be submitted. Please try again or contact nuget support.</value> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two things An easy alternative may be just remove the use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to not use NuGet in the notification. |
||
</data> | ||
<data name="AccountDelete_SupportRequestTitle" xml:space="preserve"> | ||
<value>DeleteAccountRequest</value> | ||
</data> | ||
<data name="PackageHasNoOwnersMessage" xml:space="preserve"> | ||
<value>This package has no owners and is not being actively maintained.</value> | ||
</data> | ||
</root> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
|
||
namespace NuGetGallery | ||
{ | ||
public class DeleteAccountViewModel | ||
{ | ||
public List<ListPackageItemViewModel> Packages { get; set; } | ||
|
||
public User User { get; set; } | ||
|
||
public string AccountName { get; set; } | ||
|
||
public bool HasOrphanPackages { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,20 +59,31 @@ | |
} | ||
else | ||
{ | ||
@ViewHelpers.AlertWarning( | ||
@<text> | ||
Sorry, the owners of this package do not allow contacting them through this form. | ||
if ((bool)ViewData["HasOwners"]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
{ | ||
@ViewHelpers.AlertWarning( | ||
@<text> | ||
Sorry, the owners of this package do not allow contacting them through this form. | ||
|
||
@if (string.IsNullOrEmpty(Model.ProjectUrl)) | ||
{ | ||
@:You can try contacting the package owners of "@Model.PackageId" through its <a href="@Model.ProjectUrl">Project Site</a> | ||
} | ||
else | ||
{ | ||
@:You'll have to find some other way to contact them. | ||
} | ||
</text> | ||
); | ||
@if (string.IsNullOrEmpty(Model.ProjectUrl)) | ||
{ | ||
@:You can try contacting the package owners of "@Model.PackageId" through its <a href="@Model.ProjectUrl">Project Site</a> | ||
} | ||
else | ||
{ | ||
@:You'll have to find some other way to contact them. | ||
} | ||
</text> | ||
); | ||
} | ||
else | ||
{ | ||
@ViewHelpers.AlertWarning( | ||
@<text> | ||
Sorry, this package has no owners and is not being actively maintained. You can try to find a contact for "@Model.PackageId" through its <a href="@Model.ProjectUrl">Project Site</a> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. check that the package has a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated |
||
</text> | ||
); | ||
} | ||
} | ||
</div> | ||
</div> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spacing looks off here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated