From f1ed482c26df6e242b1e4eca0948dc77ba5b8fa9 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 17 Jan 2018 11:58:38 -0800 Subject: [PATCH] Add PersonalData tab/functionality --- .../Pages/Account/ExternalLogin.cshtml | 2 +- .../Pages/Account/ForgotPassword.cshtml | 2 +- .../Areas/Identity/Pages/Account/Login.cshtml | 2 +- .../Pages/Account/LoginWith2fa.cshtml | 4 +- .../Account/LoginWithRecoveryCode.cshtml | 6 +- .../Account/Manage/ChangePassword.cshtml | 2 +- .../Account/Manage/DeletePersonalData.cshtml | 34 ++++++++ .../Manage/DeletePersonalData.cshtml.cs | 84 +++++++++++++++++++ .../Pages/Account/Manage/Disable2fa.cshtml | 2 +- .../Manage/DownloadPersonalData.cshtml | 12 +++ .../Manage/DownloadPersonalData.cshtml.cs | 51 +++++++++++ .../Account/Manage/EnableAuthenticator.cshtml | 4 +- .../Manage/GenerateRecoveryCodes.cshtml | 2 +- .../Pages/Account/Manage/Index.cshtml | 2 +- .../Pages/Account/Manage/ManageNavPages.cs | 12 +++ .../Pages/Account/Manage/PersonalData.cshtml | 27 ++++++ .../Account/Manage/PersonalData.cshtml.cs | 35 ++++++++ .../Account/Manage/ResetAuthenticator.cshtml | 2 +- .../Pages/Account/Manage/SetPassword.cshtml | 4 +- .../Manage/TwoFactorAuthentication.cshtml | 2 +- .../Pages/Account/Manage/_ManageNav.cshtml | 1 + .../Identity/Pages/Account/Register.cshtml | 2 +- .../Pages/Account/ResetPassword.cshtml | 2 +- 23 files changed, 276 insertions(+), 20 deletions(-) create mode 100644 src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml create mode 100644 src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs create mode 100644 src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml create mode 100644 src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs create mode 100644 src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml create mode 100644 src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs diff --git a/src/UI/Areas/Identity/Pages/Account/ExternalLogin.cshtml b/src/UI/Areas/Identity/Pages/Account/ExternalLogin.cshtml index 51c9b9264f..ba074852ee 100644 --- a/src/UI/Areas/Identity/Pages/Account/ExternalLogin.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/ExternalLogin.cshtml @@ -29,5 +29,5 @@ @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + } diff --git a/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml b/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml index cd8e98cf44..5cdfdc0a89 100644 --- a/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/ForgotPassword.cshtml @@ -22,5 +22,5 @@ @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + } diff --git a/src/UI/Areas/Identity/Pages/Account/Login.cshtml b/src/UI/Areas/Identity/Pages/Account/Login.cshtml index a486f27545..da09191ae0 100644 --- a/src/UI/Areas/Identity/Pages/Account/Login.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Login.cshtml @@ -78,5 +78,5 @@ @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + } diff --git a/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml b/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml index 032ecd98cd..c960eb8b22 100644 --- a/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/LoginWith2fa.cshtml @@ -32,10 +32,10 @@

- Don't have access to your authenticator device? You can + Don't have access to your authenticator device? You can log in with a recovery code.

@section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + } \ No newline at end of file diff --git a/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml b/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml index 4b32c1d8ff..abaad2a4d4 100644 --- a/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml @@ -23,7 +23,7 @@ - - @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + +@section Scripts { + } \ No newline at end of file diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml index 6aa47234d1..c35de0c622 100644 --- a/src/UI/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml @@ -31,5 +31,5 @@ @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + } diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml new file mode 100644 index 0000000000..709701fc75 --- /dev/null +++ b/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml @@ -0,0 +1,34 @@ +@page +@model DeletePersonalDataModel +@{ + ViewData["Title"] = "Delete Personal Data"; + ViewData["ActivePage"] = ManageNavPages.DeletePersonalData; +} + +

@ViewData["Title"]

+ + + +
+
+
+ @if (Model.RequirePassword) + { +
+ + + +
+ } + +
+
+ +@section Scripts { + +} diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs b/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs new file mode 100644 index 0000000000..12b227de58 --- /dev/null +++ b/src/UI/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs @@ -0,0 +1,84 @@ +// 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.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage +{ + public class DeletePersonalDataModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public DeletePersonalDataModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + } + + public bool RequirePassword { get; set; } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + RequirePassword = await _userManager.HasPasswordAsync(user); + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + RequirePassword = await _userManager.HasPasswordAsync(user); + if (RequirePassword) + { + if (!await _userManager.CheckPasswordAsync(user, Input.Password)) + { + ModelState.AddModelError(string.Empty, "Password not correct."); + return Page(); + } + } + + var result = await _userManager.DeleteAsync(user); + if (!result.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred deleteing user with ID '{user.Id}'."); + } + + await _signInManager.SignOutAsync(); + + _logger.LogInformation("User with ID '{UserId}' deleted themselves.", _userManager.GetUserId(User)); + + return Redirect("~/"); + } + } +} diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml index 954093ee3b..28bc1c5add 100644 --- a/src/UI/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml @@ -2,7 +2,7 @@ @model Disable2faModel @{ ViewData["Title"] = "Disable two-factor authentication (2FA)"; - ViewData["ActivePage"] = "TwoFactorAuthentication"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; }

@ViewData["Title"]

diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml new file mode 100644 index 0000000000..a4f5ca4814 --- /dev/null +++ b/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml @@ -0,0 +1,12 @@ +@page +@model DownloadPersonalDataModel +@{ + ViewData["Title"] = "Download Your Data"; + ViewData["ActivePage"] = ManageNavPages.DownloadPersonalData; +} + +

@ViewData["Title"]

+ +@section Scripts { + +} diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs b/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs new file mode 100644 index 0000000000..14cf888d8c --- /dev/null +++ b/src/UI/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs @@ -0,0 +1,51 @@ +// 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.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage +{ + public class DownloadPersonalDataModel : PageModel + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public DownloadPersonalDataModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + _logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User)); + + // Only include personal data for download + var personalData = new Dictionary(); + personalData.Add("UserId", await _userManager.GetUserIdAsync(user)); + personalData.Add("UserName", await _userManager.GetUserNameAsync(user)); + personalData.Add("Email", await _userManager.GetEmailAsync(user)); + personalData.Add("EmailConfirmed", (await _userManager.IsEmailConfirmedAsync(user)).ToString()); + personalData.Add("PhoneNumber", await _userManager.GetPhoneNumberAsync(user)); + personalData.Add("PhoneNumberConfirmed", (await _userManager.IsEmailConfirmedAsync(user)).ToString()); + + Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json"); + return new FileContentResult(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(personalData)), "text/json"); + } + } +} diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml index 9d1113d779..047c6388cb 100644 --- a/src/UI/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml @@ -2,7 +2,7 @@ @model EnableAuthenticatorModel @{ ViewData["Title"] = "Configure authenticator app"; - ViewData["ActivePage"] = "TwoFactorAuthentication"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; }

@ViewData["Title"]

@@ -49,5 +49,5 @@ @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + } diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml index 5966480d05..d6a39d1937 100644 --- a/src/UI/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml @@ -2,7 +2,7 @@ @model GenerateRecoveryCodesModel @{ ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes"; - ViewData["ActivePage"] = "TwoFactorAuthentication"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; }

@ViewData["Title"]

diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/Index.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/Index.cshtml index 78a0383cd3..4fc98a0c14 100644 --- a/src/UI/Areas/Identity/Pages/Account/Manage/Index.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Manage/Index.cshtml @@ -41,5 +41,5 @@ @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + } diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs b/src/UI/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs index 42e859a203..49a721ac60 100644 --- a/src/UI/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs +++ b/src/UI/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs @@ -12,16 +12,28 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage public static string ChangePassword => "ChangePassword"; + public static string DownloadPersonalData => "DownloadPersonalData"; + + public static string DeletePersonalData => "DeletePersonalData"; + public static string ExternalLogins => "ExternalLogins"; + public static string PersonalData => "PersonalData"; + public static string TwoFactorAuthentication => "TwoFactorAuthentication"; public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); + public static string DownloadPersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DownloadPersonalData); + + public static string DeletePersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DeletePersonalData); + public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); + public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData); + public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); public static string PageNavClass(ViewContext viewContext, string page) diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml new file mode 100644 index 0000000000..770f044cf0 --- /dev/null +++ b/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml @@ -0,0 +1,27 @@ +@page +@model PersonalDataModel +@{ + ViewData["Title"] = "Personal Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ +
+
+

Your account contains personal data that you have given us. This page allows you to download or delete that data.

+

+ Deleting this data will permanently remove your account, and this cannot be recovered. +

+
+ +
+

+ Delete +

+
+
+ +@section Scripts { + +} diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs b/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs new file mode 100644 index 0000000000..432371ff6d --- /dev/null +++ b/src/UI/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs @@ -0,0 +1,35 @@ +// 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.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage +{ + public class PersonalDataModel : PageModel + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public PersonalDataModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + return Page(); + } + } +} diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml index a9534ae257..0bb89805b7 100644 --- a/src/UI/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml @@ -2,7 +2,7 @@ @model ResetAuthenticatorModel @{ ViewData["Title"] = "Reset authenticator key"; - ViewData["ActivePage"] = "TwoFactorAuthentication"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; }

@ViewData["Title"]

diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml index 3f4544caf4..feeb3c30e4 100644 --- a/src/UI/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml @@ -2,7 +2,7 @@ @model SetPasswordModel @{ ViewData["Title"] = "Set password"; - ViewData["ActivePage"] = "ChangePassword"; + ViewData["ActivePage"] = ManageNavPages.ChangePassword; }

Set your password

@@ -31,5 +31,5 @@ @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + } diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml index fabe1999e6..19ba8daacb 100644 --- a/src/UI/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml @@ -45,5 +45,5 @@ else } @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + } diff --git a/src/UI/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml b/src/UI/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml index 56db638f33..5ba75b6646 100644 --- a/src/UI/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml @@ -11,5 +11,6 @@
  • External logins
  • }
  • Two-factor authentication
  • +
  • Personal data
  • diff --git a/src/UI/Areas/Identity/Pages/Account/Register.cshtml b/src/UI/Areas/Identity/Pages/Account/Register.cshtml index 64f2151518..d40f55c35c 100644 --- a/src/UI/Areas/Identity/Pages/Account/Register.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/Register.cshtml @@ -33,5 +33,5 @@ @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + } diff --git a/src/UI/Areas/Identity/Pages/Account/ResetPassword.cshtml b/src/UI/Areas/Identity/Pages/Account/ResetPassword.cshtml index 85a81b1ef5..5ccb61edcf 100644 --- a/src/UI/Areas/Identity/Pages/Account/ResetPassword.cshtml +++ b/src/UI/Areas/Identity/Pages/Account/ResetPassword.cshtml @@ -33,5 +33,5 @@ @section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") + }