484 lines
17 KiB
C#
484 lines
17 KiB
C#
using Microsoft.AspNet.Abstractions;
|
|
using Microsoft.AspNet.Abstractions.Security;
|
|
using Microsoft.AspNet.Identity;
|
|
using Microsoft.AspNet.Identity.InMemory;
|
|
using Microsoft.AspNet.Mvc;
|
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
|
using MusicStore.Models;
|
|
using System.Security.Principal;
|
|
using System.Threading.Tasks;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace MusicStore.Controllers
|
|
{
|
|
//[Authorize]
|
|
public class AccountController : Controller
|
|
{
|
|
public AccountController()
|
|
//Bug: No EF yet - using an in memory store
|
|
//: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
|
|
: this(new UserManager<ApplicationUser, string>(new InMemoryUserStore<ApplicationUser>()))
|
|
{
|
|
}
|
|
|
|
public AccountController(UserManager<ApplicationUser, string> userManager)
|
|
{
|
|
UserManager = userManager;
|
|
}
|
|
|
|
public UserManager<ApplicationUser, string> UserManager { get; private set; }
|
|
|
|
//
|
|
// GET: /Account/Login
|
|
[AllowAnonymous]
|
|
public IActionResult Login(string returnUrl)
|
|
{
|
|
ViewBag.ReturnUrl = returnUrl;
|
|
return View();
|
|
}
|
|
|
|
//
|
|
// POST: /Account/Login
|
|
//Bug: HTTP verb attribs not available
|
|
//[HttpPost]
|
|
[AllowAnonymous]
|
|
//[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl)
|
|
{
|
|
if (ModelState.IsValid == true)
|
|
{
|
|
var user = await UserManager.Find(model.UserName, model.Password);
|
|
if (user != null)
|
|
{
|
|
await SignIn(user, model.RememberMe);
|
|
return RedirectToLocal(returnUrl);
|
|
}
|
|
else
|
|
{
|
|
ModelState.AddModelError("", "Invalid username or password.");
|
|
}
|
|
}
|
|
|
|
// If we got this far, something failed, redisplay form
|
|
return View(model);
|
|
}
|
|
|
|
//
|
|
// GET: /Account/Register
|
|
[AllowAnonymous]
|
|
public IActionResult Register()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
//
|
|
// POST: /Account/Register
|
|
//Bug: Missing verb attributes
|
|
//[HttpPost]
|
|
[AllowAnonymous]
|
|
//[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> Register(RegisterViewModel model)
|
|
{
|
|
if (ModelState.IsValid == true)
|
|
{
|
|
var user = new ApplicationUser() { UserName = model.UserName };
|
|
var result = await UserManager.Create(user, model.Password);
|
|
if (result.Succeeded)
|
|
{
|
|
await SignIn(user, isPersistent: false);
|
|
//Bug: No helper methods
|
|
//return RedirectToAction("Index", "Home");
|
|
}
|
|
else
|
|
{
|
|
AddErrors(result);
|
|
}
|
|
}
|
|
|
|
// If we got this far, something failed, redisplay form
|
|
return View(model);
|
|
}
|
|
|
|
//
|
|
// POST: /Account/Disassociate
|
|
//Bug: HTTP verbs
|
|
//[HttpPost]
|
|
//[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> Disassociate(string loginProvider, string providerKey)
|
|
{
|
|
|
|
ManageMessageId? message = null;
|
|
IdentityResult result = await UserManager.RemoveLogin(this.Context.User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
|
|
if (result.Succeeded)
|
|
{
|
|
message = ManageMessageId.RemoveLoginSuccess;
|
|
}
|
|
else
|
|
{
|
|
message = ManageMessageId.Error;
|
|
}
|
|
//Bug: No helpers available
|
|
//return RedirectToAction("Manage", new { Message = message });
|
|
return View();
|
|
}
|
|
|
|
//
|
|
// GET: /Account/Manage
|
|
public IActionResult Manage(ManageMessageId? message)
|
|
{
|
|
ViewBag.StatusMessage =
|
|
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
|
|
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
|
|
: message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
|
|
: message == ManageMessageId.Error ? "An error has occurred."
|
|
: "";
|
|
ViewBag.HasLocalPassword = HasPassword();
|
|
//Bug: No Action method with single parameter
|
|
//ViewBag.ReturnUrl = Url.Action("Manage");
|
|
//ViewBag.ReturnUrl = Url.Action("Manage", "Account", null);
|
|
return View();
|
|
}
|
|
|
|
//
|
|
// POST: /Account/Manage
|
|
//Bug: No verb attributes
|
|
//[HttpPost]
|
|
//[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> Manage(ManageUserViewModel model)
|
|
{
|
|
bool hasPassword = await HasPassword();
|
|
ViewBag.HasLocalPassword = hasPassword;
|
|
//Bug: No Action method with single parameter
|
|
//ViewBag.ReturnUrl = Url.Action("Manage");
|
|
//ViewBag.ReturnUrl = Url.Action("Manage", "Account", null);
|
|
if (hasPassword)
|
|
{
|
|
if (ModelState.IsValid == true)
|
|
{
|
|
IdentityResult result = await UserManager.ChangePassword(this.Context.User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
|
|
if (result.Succeeded)
|
|
{
|
|
//Bug: No helper method
|
|
//return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
|
|
return View();
|
|
}
|
|
else
|
|
{
|
|
AddErrors(result);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// User does not have a password so remove any validation errors caused by a missing OldPassword field
|
|
ModelState state = null;
|
|
ModelState.TryGetValue("OldPassword", out state);
|
|
|
|
if (state != null)
|
|
{
|
|
state.Errors.Clear();
|
|
}
|
|
|
|
if (ModelState.IsValid == true)
|
|
{
|
|
IdentityResult result = await UserManager.AddPassword(this.Context.User.Identity.GetUserId(), model.NewPassword);
|
|
if (result.Succeeded)
|
|
{
|
|
//Bug: No helper method
|
|
//return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
|
|
}
|
|
else
|
|
{
|
|
AddErrors(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we got this far, something failed, redisplay form
|
|
return View(model);
|
|
}
|
|
|
|
//
|
|
// POST: /Account/ExternalLogin
|
|
//Bug: No verb attributes
|
|
//[HttpPost]
|
|
[AllowAnonymous]
|
|
//[ValidateAntiForgeryToken]
|
|
public IActionResult ExternalLogin(string provider, string returnUrl)
|
|
{
|
|
// Request a redirect to the external login provider
|
|
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
|
|
}
|
|
|
|
//
|
|
// GET: /Account/ExternalLoginCallback
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
|
|
{
|
|
var loginInfo = await this.Context.Response.GetExternalLoginInfo();
|
|
if (loginInfo == null)
|
|
{
|
|
//Bug: No helper
|
|
//return RedirectToAction("Login");
|
|
return View();
|
|
}
|
|
|
|
// Sign in the user with this external login provider if the user already has a login
|
|
var user = await UserManager.Find(loginInfo.Login);
|
|
if (user != null)
|
|
{
|
|
await SignIn(user, isPersistent: false);
|
|
return RedirectToLocal(returnUrl);
|
|
}
|
|
else
|
|
{
|
|
// If the user does not have an account, then prompt the user to create an account
|
|
ViewBag.ReturnUrl = returnUrl;
|
|
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
|
|
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName = loginInfo.DefaultUserName });
|
|
}
|
|
}
|
|
|
|
//
|
|
// POST: /Account/LinkLogin
|
|
//Bug: No HTTP verbs
|
|
//[HttpPost]
|
|
//[ValidateAntiForgeryToken]
|
|
public IActionResult LinkLogin(string provider)
|
|
{
|
|
// Request a redirect to the external login provider to link a login for the current user
|
|
return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account", null), this.Context.User.Identity.GetUserId());
|
|
}
|
|
|
|
//
|
|
// GET: /Account/LinkLoginCallback
|
|
public async Task<IActionResult> LinkLoginCallback()
|
|
{
|
|
var loginInfo = await this.Context.Response.GetExternalLoginInfo(XsrfKey, this.Context.User.Identity.GetUserId());
|
|
if (loginInfo == null)
|
|
{
|
|
//Bug: No helper method
|
|
//return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
|
|
return View();
|
|
}
|
|
var result = await UserManager.AddLogin(this.Context.User.Identity.GetUserId(), loginInfo.Login);
|
|
if (result.Succeeded)
|
|
{
|
|
//Bug: No helper method
|
|
//return RedirectToAction("Manage");
|
|
return View();
|
|
}
|
|
//Bug: No helper method
|
|
//return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
|
|
return View();
|
|
}
|
|
|
|
//
|
|
// POST: /Account/ExternalLoginConfirmation
|
|
//Bug: No HTTP verbs
|
|
//[HttpPost]
|
|
[AllowAnonymous]
|
|
//[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
|
|
{
|
|
if (this.Context.User.Identity.IsAuthenticated)
|
|
{
|
|
//Bug: No helper yet
|
|
//return RedirectToAction("Manage");
|
|
return View();
|
|
}
|
|
|
|
if (ModelState.IsValid == true)
|
|
{
|
|
// Get the information about the user from the external login provider
|
|
var info = await this.Context.Response.GetExternalLoginInfo();
|
|
if (info == null)
|
|
{
|
|
return View("ExternalLoginFailure");
|
|
}
|
|
|
|
var user = new ApplicationUser() { UserName = model.UserName };
|
|
var result = await UserManager.Create(user);
|
|
if (result.Succeeded)
|
|
{
|
|
result = await UserManager.AddLogin(user.Id, info.Login);
|
|
if (result.Succeeded)
|
|
{
|
|
await SignIn(user, isPersistent: false);
|
|
return RedirectToLocal(returnUrl);
|
|
}
|
|
}
|
|
AddErrors(result);
|
|
}
|
|
|
|
ViewBag.ReturnUrl = returnUrl;
|
|
return View(model);
|
|
}
|
|
|
|
//
|
|
// POST: /Account/LogOff
|
|
//Bug: No HTTP verbs
|
|
//[HttpPost]
|
|
//[ValidateAntiForgeryToken]
|
|
public IActionResult LogOff()
|
|
{
|
|
this.Context.Response.SignOut();
|
|
//Bug: No helper
|
|
//return RedirectToAction("Index", "Home");
|
|
return View();
|
|
}
|
|
|
|
//
|
|
// GET: /Account/ExternalLoginFailure
|
|
[AllowAnonymous]
|
|
public IActionResult ExternalLoginFailure()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
//Bug: Need this attribute
|
|
//[ChildActionOnly]
|
|
public async Task<IActionResult> RemoveAccountList()
|
|
{
|
|
var linkedAccounts = await UserManager.GetLogins(this.Context.User.Identity.GetUserId());
|
|
ViewBag.ShowRemoveButton = await HasPassword() || linkedAccounts.Count > 1;
|
|
//Bug: We dont have partial views yet
|
|
//return (IActionResult)PartialView("_RemoveAccountPartial", linkedAccounts);
|
|
return View();
|
|
}
|
|
|
|
//Bug: Controllers need to be disposable?
|
|
protected void Dispose(bool disposing)
|
|
{
|
|
if (disposing && UserManager != null)
|
|
{
|
|
UserManager.Dispose();
|
|
UserManager = null;
|
|
}
|
|
|
|
//base.Dispose(disposing);
|
|
}
|
|
|
|
#region Helpers
|
|
// Used for XSRF protection when adding external logins
|
|
private const string XsrfKey = "XsrfId";
|
|
|
|
private async Task SignIn(ApplicationUser user, bool isPersistent)
|
|
{
|
|
this.Context.Response.SignOut(DefaultAuthenticationTypes.ExternalCookie);
|
|
var identity = await UserManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
|
|
this.Context.Response.SignIn(identity, new AuthenticationProperties() { IsPersistent = isPersistent });
|
|
}
|
|
|
|
private void AddErrors(IdentityResult result)
|
|
{
|
|
foreach (var error in result.Errors)
|
|
{
|
|
ModelState.AddModelError("", error);
|
|
}
|
|
}
|
|
|
|
private async Task<bool> HasPassword()
|
|
{
|
|
var user = await UserManager.FindById(this.Context.User.Identity.GetUserId());
|
|
if (user != null)
|
|
{
|
|
return user.PasswordHash != null;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public enum ManageMessageId
|
|
{
|
|
ChangePasswordSuccess,
|
|
SetPasswordSuccess,
|
|
RemoveLoginSuccess,
|
|
Error
|
|
}
|
|
|
|
private IActionResult RedirectToLocal(string returnUrl)
|
|
{
|
|
//Bug: No helpers available
|
|
//if (Url.IsLocalUrl(returnUrl))
|
|
//{
|
|
// return Redirect(returnUrl);
|
|
//}
|
|
//else
|
|
//{
|
|
// return RedirectToAction("Index", "Home");
|
|
//}
|
|
return View();
|
|
}
|
|
|
|
private class ChallengeResult : HttpStatusCodeResult
|
|
{
|
|
public ChallengeResult(string provider, string redirectUri)
|
|
: this(provider, redirectUri, null)
|
|
{
|
|
}
|
|
|
|
public ChallengeResult(string provider, string redirectUri, string userId)
|
|
: base(401)
|
|
{
|
|
LoginProvider = provider;
|
|
RedirectUri = redirectUri;
|
|
UserId = userId;
|
|
}
|
|
|
|
public string LoginProvider { get; set; }
|
|
public string RedirectUri { get; set; }
|
|
public string UserId { get; set; }
|
|
|
|
new public void ExecuteResultAsync(ActionContext context)
|
|
{
|
|
var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
|
|
if (UserId != null)
|
|
{
|
|
properties.Dictionary[XsrfKey] = UserId;
|
|
}
|
|
|
|
context.HttpContext.Response.Challenge(LoginProvider, properties);
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// TODO: Temporary APIs to unblock build. Need to remove this once we have these APIs available.
|
|
/// </summary>
|
|
public static class Extensions
|
|
{
|
|
public static string GetUserId(this IIdentity user)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
public static Task<ExternalLoginInfo> GetExternalLoginInfo(this HttpResponse response)
|
|
{
|
|
return Task.FromResult<ExternalLoginInfo>(new ExternalLoginInfo());
|
|
}
|
|
|
|
public static Task<ExternalLoginInfo> GetExternalLoginInfo(this HttpResponse response, string xsrfKey, string expectedValue)
|
|
{
|
|
return Task.FromResult<ExternalLoginInfo>(new ExternalLoginInfo());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// TODO: Temporary APIs to unblock build. Need to remove this once we have these APIs available.
|
|
/// </summary>
|
|
public class ExternalLoginInfo
|
|
{
|
|
public string DefaultUserName { get; set; }
|
|
public UserLoginInfo Login { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// TODO: Temporary APIs to unblock build. Need to remove this once we have these APIs available.
|
|
/// </summary>
|
|
public static class DefaultAuthenticationTypes
|
|
{
|
|
public const string ApplicationCookie = "Application";
|
|
public const string ExternalCookie = "External";
|
|
}
|
|
} |