Skip to content
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

Merged
merged 6 commits into from
Nov 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/NuGetGallery/App_Start/Routes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,14 @@ public static void RegisterUIRoutes(RouteCollection routes)

routes.MapRoute(
RouteName.AdminDeleteAccount,
"account/delete/{accountName}",
"account/delete/{accountName}",
new { controller = "Users", action = "Delete" });

routes.MapRoute(
RouteName.UserDeleteAccount,
"account/delete",
new { controller = "Users", action = "DeleteRequest" });

routes.MapRoute(
RouteName.Account,
"account/{action}",
Expand Down
10 changes: 5 additions & 5 deletions src/NuGetGallery/Areas/Admin/Models/IssueStatusKeys.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
namespace NuGetGallery.Areas.Admin.Models
{
internal class IssueStatusKeys
public class IssueStatusKeys
{
internal const int New = 0;
internal const int Working = 1;
internal const int WaitingForCustomer = 2;
internal const int Resolved = 3;
public const int New = 0;
public const int Working = 1;
public const int WaitingForCustomer = 2;
public const int Resolved = 3;

/// <summary>
/// Does not exist in database.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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;
using System.Collections.Generic;
using System.Threading.Tasks;
using NuGetGallery.Areas.Admin.Models;
Expand Down Expand Up @@ -33,7 +34,7 @@ public interface ISupportRequestService
/// <returns>Returns a <see cref="IReadOnlyCollection{Issue}"/> that matches the provided filter parameters.</returns>
IReadOnlyCollection<Issue> GetIssues(int? assignedTo = null, string reason = null, int? issueStatusId = null, string galleryUsername = null);

Task AddNewSupportRequestAsync(string subject, string message, string requestorEmailAddress, string reason,
Task<bool> AddNewSupportRequestAsync(string subject, string message, string requestorEmailAddress, string reason,
User user, Package package = null);
Task UpdateIssueAsync(int issueId, int? assignedToId, int issueStatusId, string comment, string editedBy);
int GetIssueCount(int? assignedToId, string reason, int? issueStatusId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,11 @@ public async Task UpdateIssueAsync(int issueId, int? assignedToId, int issueStat
}
}

public async Task AddNewSupportRequestAsync(string subject, string message, string requestorEmailAddress, string reason,
public async Task<bool> AddNewSupportRequestAsync(string subject, string message, string requestorEmailAddress, string reason,
User user, Package package = null)
{
var loggedInUser = user?.Username ?? "Anonymous";
bool result = true;

try
{
Expand Down Expand Up @@ -236,6 +237,7 @@ public async Task AddNewSupportRequestAsync(string subject, string message, stri
catch (SqlException sqlException)
{
QuietLog.LogHandledException(sqlException);
result = false;

var packageInfo = "N/A";
if (package != null)
Expand All @@ -249,8 +251,10 @@ public async Task AddNewSupportRequestAsync(string subject, string message, stri
}
catch (Exception e) //In case getting data from PagerDuty has failed
{
result = false;
QuietLog.LogHandledException(e);
}
return result;
}

private async Task AddIssueAsync(Issue issue)
Expand Down
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
Expand Up @@ -29,7 +29,7 @@
</div>
</div>
<div class="form-group danger-zone">
@using (Html.BeginForm("Delete", "Users", new { id = "delete-form" }))
@using (Html.BeginForm("Delete", "Users", FormMethod.Post, new { id = "delete-form" }))
{
@Html.HttpMethodOverride(HttpVerbs.Delete)
@Html.AntiForgeryToken()
Expand Down Expand Up @@ -71,7 +71,7 @@
@section BottomScripts {
<script type="text/javascript">
$(function () {
$('#delete-form').submit(function(e) {
$('#delete-form').submit(function (e) {
if (!confirm('Are you sure you want to continue to delete this account?')) {
e.preventDefault();
}
Expand Down
2 changes: 2 additions & 0 deletions src/NuGetGallery/Controllers/PackagesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -767,12 +767,14 @@ public virtual ActionResult ContactOwners(string id)
return HttpNotFound();
}

bool hasOwners = package.PackageRegistration.Owners.Any();
var model = new ContactOwnersViewModel
{
PackageId = package.PackageRegistration.Id,
ProjectUrl = package.ProjectUrl,
Owners = package.PackageRegistration.Owners.Where(u => u.EmailAllowed),
CopySender = true,
HasOwners = hasOwners
};

return View(model);
Expand Down
66 changes: 65 additions & 1 deletion src/NuGetGallery/Controllers/UsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Net.Mail;
using System.Threading.Tasks;
using System.Web.Mvc;
using NuGetGallery.Areas.Admin;
using NuGetGallery.Areas.Admin.Models;
using NuGetGallery.Areas.Admin.ViewModels;
using NuGetGallery.Authentication;
using NuGetGallery.Configuration;
Expand All @@ -28,6 +30,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,
Expand All @@ -38,7 +41,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));
Expand All @@ -49,6 +53,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]
Expand Down Expand Up @@ -94,6 +99,65 @@ 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.");
}

var listPackageItems = _packageService
.FindPackagesByOwner(user, includeUnlisted: true)
.Select(p => new ListPackageItemViewModel(p))
.ToList();

bool hasPendingRequest = _supportRequestService.GetIssues().Where((issue)=> string.Equals(issue.CreatedBy, user.Username) &&
string.Equals(issue.IssueTitle, Strings.AccountDelete_SupportRequestTitle) &&
issue.Key != IssueStatusKeys.Resolved).Any();

var model = new DeleteAccountViewModel()
{
Packages = listPackageItems,
User = user,
AccountName = user.Username,
HasOrphanPackages = listPackageItems.Any(p => p.Owners.Count <= 1),
HasPendingRequests = hasPendingRequest
};

return View("DeleteAccount", model);
}

[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public virtual async Task<ActionResult> RequestAccountDeletion()
{
var user = GetCurrentUser();

if (user == null || user.IsDeleted)
{
return HttpNotFound("User not found.");
}

bool isSupportRequestCreated = await _supportRequestService.AddNewSupportRequestAsync(Strings.AccountDelete_SupportRequestTitle,
Strings.AccountDelete_SupportRequestTitle,
user.EmailAddress,
"The user requested to have the account deleted.",
user);
if (!isSupportRequestCreated)
{
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)
Expand Down
2 changes: 2 additions & 0 deletions src/NuGetGallery/NuGetGallery.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,7 @@
<Compile Include="Services\ReflowPackageService.cs" />
<Compile Include="Services\TelemetryService.cs" />
<Compile Include="Areas\Admin\ViewModels\DeleteUserAccountViewModel.cs" />
<Compile Include="ViewModels\DeleteAccountViewModel.cs" />
<Compile Include="ViewModels\PackageOwnersResultViewModel.cs" />
<Compile Include="ViewModels\ManagePackagesListViewModel.cs" />
<Compile Include="ViewModels\ApiKeyListViewModel.cs" />
Expand Down Expand Up @@ -1955,6 +1956,7 @@
<Content Include="Areas\Admin\Views\DeleteAccount\DeleteUserAccount.cshtml" />
<Content Include="Views\Users\_UserPackagesListForDeletedAccount.cshtml" />
<Content Include="Views\Users\_ReservedNamespacesList.cshtml" />
<Content Include="Views\Users\DeleteAccount.cshtml" />
<Content Include="Views\Authentication\SignInNuGetAccount.cshtml" />
</ItemGroup>
<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions src/NuGetGallery/RouteNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,6 @@ public static class RouteName
public const string JsonApi = "JsonApi";
public const string Downloads = "Downloads";
public const string AdminDeleteAccount = "AdminDeleteAccount";
public const string UserDeleteAccount = "DeleteAccount";
}
}
1 change: 1 addition & 0 deletions src/NuGetGallery/Services/IMessageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ public interface IMessageService
void SendCredentialAddedNotice(User user, Credential added);
void SendContactSupportEmail(ContactSupportRequest request);
void SendPackageAddedNotice(Package package, string packageUrl, string packageSupportUrl, string emailSettingsUrl);
void SendAccountDeleteNotice(MailAddress mailAddress, string userName);
}
}
30 changes: 30 additions & 0 deletions src/NuGetGallery/Services/MessageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,36 @@ [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 immediately.
{2}When your account will be deleted, we will:{2}
- revoke your API key(s)
- remove you as the owner for any package you own
- remove your ownership from any ID prefix reservations and delete any ID prefix reservations that you were the only owner of

{2}We will not delete the NuGet packages associated with the account.

Thanks,
{2}The {1} Team";

body = String.Format(
CultureInfo.CurrentCulture,
body,
account,
Config.GalleryOwner.DisplayName,
Environment.NewLine);

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))
Expand Down
18 changes: 18 additions & 0 deletions src/NuGetGallery/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/NuGetGallery/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -618,4 +618,10 @@ 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 support.</value>
</data>
<data name="AccountDelete_SupportRequestTitle" xml:space="preserve">
<value>DeleteAccountRequest</value>
</data>
</root>
2 changes: 2 additions & 0 deletions src/NuGetGallery/ViewModels/ContactOwnersViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ public class ContactOwnersViewModel
[Required(ErrorMessage = "Please enter a message.")]
[StringLength(4000)]
public string Message { get; set; }

public bool HasOwners { get; set; }
}
}
20 changes: 20 additions & 0 deletions src/NuGetGallery/ViewModels/DeleteAccountViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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; }

public bool HasPendingRequests { get; set; }
}
}
Loading