using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; using EnVisage.Code.BLL; using EnVisage.Properties; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.Owin.Security; using EnVisage.Models; using EnVisage.Code; using System.Net; using System.DirectoryServices; using System.DirectoryServices.AccountManagement; namespace EnVisage.Controllers { [Authorize] public class AccountController : BaseController { public AccountController() : this(new UserManager(new UserStore(new ApplicationDbContext()))) { } public AccountController(UserManager userManager) { UserManager = userManager; } public UserManager UserManager { get; private set; } // // GET: /Account/Login [AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.ReturnUrl = returnUrl; return View(); } // // POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task Login(LoginViewModel model, string returnUrl) { if (ModelState.IsValid) { var user = await UserManager.FindAsync(model.UserName, model.Password); if (user != null) { if (user.Type == (int)Code.UserType.Active) { await SignInAsync(user, model.RememberMe); return RedirectToLocal(returnUrl); } else ModelState.AddModelError("", "Sorry, your account is not active."); } else { //var LDAPLogin="planit\\serverLogin"; //For directory lookup //var LDAPPass = "serverPass"; //DirectoryEntry entry = new DirectoryEntry("LDAP://planitserver/OU=People,O=planit", LDAPLogin, LDAPPass); // //DirectorySearcher search = new DirectorySearcher( // // entry, // // "(uid=" + "planit\\" + model.UserName + ")", // // new string[] { "uid" } // //); // //search.SearchScope = System.DirectoryServices.SearchScope.Subtree; // //SearchResult found = search.FindOne(); // //if (found == null){ // // ModelState.AddModelError("", "Invalid username or password."); // // return View(model); // //} //bool result; //using(var context = new PrincipalContext(ContextType.Domain, "planit", LDAPLogin, LDAPPass)) { // //Username and password for authentication. // result = context.ValidateCredentials("planit\\" + model.UserName, model.Password); //} //if (result) //{ // var newuser = new ApplicationUser(); // newuser.UserName = model.UserName; // var creationResult = await UserManager.CreateAsync(user, model.Password); // if (creationResult.Succeeded) // creationResult = UserManager.AddToRole(newuser.Id, "User"); // await SignInAsync(newuser, 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] [Obsolete] public ActionResult Register() { return View(); } // // POST: /Account/Register [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] [Obsolete] public async Task Register(RegisterViewModel model) { if (ModelState.IsValid) { var user = new ApplicationUser() { UserName = model.UserName, PagePreferences = string.Empty}; var result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { await SignInAsync(user, isPersistent: false); return RedirectToAction("Index", "Home"); } else { AddErrors(result); } } // If we got this far, something failed, redisplay form return View(model); } // // POST: /Account/Disassociate [HttpPost] [ValidateAntiForgeryToken] [Obsolete] public async Task Disassociate(string loginProvider, string providerKey) { ManageMessageId? message = null; IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey)); if (result.Succeeded) { message = ManageMessageId.RemoveLoginSuccess; } else { message = ManageMessageId.Error; } return RedirectToAction("Manage", new { Message = message }); } // // GET: /Account/Manage [Obsolete] public ActionResult 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(); ViewBag.ReturnUrl = Url.Action("Manage"); var userId = User.Identity.GetUserId(); var user = DbContext.AspNetUsers.Find(userId); var model = new ManageUserViewModel() { Email = user.Email, Phone = user.Phone, PreferredResourceAllocation = user.PreferredResourceAllocation, }; return View(model); } // // POST: /Account/Manage [HttpPost] [ValidateAntiForgeryToken] [Obsolete] public async Task Manage(ManageUserViewModel model) { bool hasPassword = HasPassword(); ViewBag.HasLocalPassword = hasPassword; ViewBag.ReturnUrl = Url.Action("Manage"); if (hasPassword) { if (ModelState.IsValid) { IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword); if (result.Succeeded) { return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess }); } else { AddErrors(result); } } } else { // User does not have a password so remove any validation errors caused by a missing OldPassword field ModelState state = ModelState["OldPassword"]; if (state != null) { state.Errors.Clear(); } if (ModelState.IsValid) { IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword); if (result.Succeeded) { return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess }); } else { AddErrors(result); } } } // If we got this far, something failed, redisplay form return View(model); } // // POST: /Account/ChangeEmail [HttpPost] [Obsolete] public JsonResult ChangeEmailAndPhone(string newEmail, string newPhone, bool newPreferredResourceAllocation) { try { var userId = User.Identity.GetUserId(); var user = UserManager.FindById(userId); if (user.Email != newEmail) user.Email = newEmail; if (user.Phone != newPhone) user.Phone = newPhone; user.PreferredResourceAllocation = newPreferredResourceAllocation; UserManager.Update(user); DbContext.SaveChanges(); } catch (Exception ex) { return Json(new { Status = "Error", Msg = ex.Message, Exception = ex.ToString() }); } return Json(new { Status = "OK" }); } // // POST: /Account/ExternalLogin [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] [Obsolete] public ActionResult 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] [Obsolete] public async Task ExternalLoginCallback(string returnUrl) { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); if (loginInfo == null) { return RedirectToAction("Login"); } // Sign in the user with this external login provider if the user already has a login var user = await UserManager.FindAsync(loginInfo.Login); if (user != null) { await SignInAsync(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 [HttpPost] [ValidateAntiForgeryToken] public ActionResult 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"), User.Identity.GetUserId()); } // // GET: /Account/LinkLoginCallback public async Task LinkLoginCallback() { var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId()); if (loginInfo == null) { return RedirectToAction("Manage", new { Message = ManageMessageId.Error }); } var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login); if (result.Succeeded) { return RedirectToAction("Manage"); } return RedirectToAction("Manage", new { Message = ManageMessageId.Error }); } // // POST: /Account/ExternalLoginConfirmation [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl) { if (User.Identity.IsAuthenticated) { return RedirectToAction("Manage"); } if (ModelState.IsValid) { // Get the information about the user from the external login provider var info = await AuthenticationManager.GetExternalLoginInfoAsync(); if (info == null) { return View("ExternalLoginFailure"); } var user = new ApplicationUser() { UserName = model.UserName, PagePreferences = string.Empty}; var result = await UserManager.CreateAsync(user); if (result.Succeeded) { result = await UserManager.AddLoginAsync(user.Id, info.Login); if (result.Succeeded) { await SignInAsync(user, isPersistent: false); return RedirectToLocal(returnUrl); } } AddErrors(result); } ViewBag.ReturnUrl = returnUrl; return View(model); } // // POST: /Account/LogOff [HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { AuthenticationManager.SignOut(); return RedirectToAction("Index", "Home"); } // // GET: /Account/ExternalLoginFailure [AllowAnonymous] public ActionResult ExternalLoginFailure() { return View(); } [ChildActionOnly] [Obsolete] public ActionResult RemoveAccountList() { var linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId()); ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1; return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts); } // // GET: /Account/Activate [AllowAnonymous] public ActionResult Activate(string userId) { var user = UserManager.FindById(userId); if (user == null) return new HttpStatusCodeResult(HttpStatusCode.BadRequest); if (user.Type != (int)UserType.Pending) ModelState.AddModelError("", "This account is already activated."); var activatedUser = new ActivationUserModel() { Email = user.Email, UserName = user.UserName, Password = string.Empty, ConfirmationPassword = string.Empty, UserId = user.Id }; return View(activatedUser); } // // POST: /Accont/Activate [AllowAnonymous] [HttpPost] public async Task Activate(ActivationUserModel model) { if (ModelState.IsValid) { if (!string.IsNullOrWhiteSpace(model.UserId)) { ApplicationDbContext cnt = new ApplicationDbContext(); var userManager = new UserManager(new UserStore(cnt)); var user = userManager.FindById(model.UserId); if (user == null) return new HttpStatusCodeResult(HttpStatusCode.BadRequest); if (user.Type != (int)UserType.Pending) ModelState.AddModelError("", "This account is already activated."); var passwordHash = userManager.PasswordHasher.HashPassword(model.Password); user.PasswordHash = passwordHash; user.Type = (int)UserType.Active; cnt.SaveChanges(); await SignInAsync(user, false); return RedirectToAction("Index", "Home"); } else ModelState.AddModelError("", "This user is not exsist."); } return View(); } [AllowAnonymous] public ActionResult ForgotPassword() { return View(new ForgotPasswordModel()); } [HttpPost] [ValidateAntiForgeryToken] [AllowAnonymous] public ActionResult ForgotPassword(ForgotPasswordModel model) { if (ModelState.IsValid) { try { using (var db = new EnVisageEntities()) { var user = db.AspNetUsers.FirstOrDefault(t => t.Email == model.Email); if (user == null) { ModelState.AddModelError("", "No matching email address found"); return View(model); } SendResetPasswordLink(user.Id, user.UserName, user.Email, db); } model.Sent = true; } catch (BLLException blEx) // handle any system specific error { // display error message if required if (blEx.DisplayError) ModelState.AddModelError(string.Empty, blEx.Message); else // if display not requried then display modal form with general error message { LogException(blEx); SetErrorScript(); } } catch (Exception exception) // handle any unexpected error { LogException(exception); SetErrorScript(); } } return View(model); } [HttpPost] public ActionResult ResetPwd(string id) { try { using (var db = new EnVisageEntities()) { var user = db.AspNetUsers.FirstOrDefault(t => t.Id == id); if (user == null) { throw new ArgumentException("No matching user Id '{0}' found", id); } SendResetPasswordLink(user.Id, user.UserName, user.Email, db); } return Json(new {status = "OK"}); } catch (Exception exception) // handle any unexpected error { LogException(exception); return Json(new {status = "ERROR"}); } } [AllowAnonymous] public ActionResult RestorePassword(Guid token) { if (Guid.Empty.Equals(token)) return new HttpNotFoundResult(); var model = new RestorePasswordModel(); try { using (var db = new EnVisageEntities()) { var request = db.PasswordResetRequests.FirstOrDefault(t => t.Token == token); if (request == null) return new HttpNotFoundResult(); if (request.ValidUntil < DateTime.Now) throw new RestorePasswordTokenExpiredException(); } } catch (BLLException blEx) // handle any system specific error { // display error message if required if (blEx.DisplayError) ModelState.AddModelError(string.Empty, blEx.Message); else // if display not requried then display modal form with general error message { LogException(blEx); SetErrorScript(); } } catch (Exception exception) // handle any unexpected error { LogException(exception); SetErrorScript(); } return View(model); } [HttpPost] [ValidateAntiForgeryToken] [AllowAnonymous] public ActionResult RestorePassword(RestorePasswordModel model) { if (ModelState.IsValid) { try { using (var db = new EnVisageEntities()) { var request = db.PasswordResetRequests.FirstOrDefault(t => t.Token == model.Token); if (request == null) return new HttpNotFoundResult(); if (request.ValidUntil < DateTime.Now) throw new RestorePasswordTokenExpiredException(); var user = db.AspNetUsers.FirstOrDefault(t => t.Id == request.UserId); if (user == null) return new HttpNotFoundResult(); db.Entry(request).State = EntityState.Deleted; UserManager.RemovePassword(user.Id); UserManager.AddPassword(user.Id, model.Password); db.SaveChanges(); } model.Restored = true; } catch (BLLException blEx) // handle any system specific error { // display error message if required if (blEx.DisplayError) ModelState.AddModelError(string.Empty, blEx.Message); else // if display not requried then display modal form with general error message { LogException(blEx); SetErrorScript(); } } catch (Exception exception) // handle any unexpected error { LogException(exception); SetErrorScript(); } } return View(model); } protected override 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 IAuthenticationManager AuthenticationManager { get { return HttpContext.GetOwinContext().Authentication; } } private async Task SignInAsync(ApplicationUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); var auth_props = new AuthenticationProperties() { IsPersistent = isPersistent }; if(isPersistent) auth_props.ExpiresUtc = DateTime.UtcNow.AddHours(12); AuthenticationManager.SignIn(auth_props, identity); } private void AddErrors(IdentityResult result) { foreach (var error in result.Errors) { ModelState.AddModelError("", error); } } private bool HasPassword() { var user = UserManager.FindById(User.Identity.GetUserId()); if (user != null) { return user.PasswordHash != null; } return false; } public enum ManageMessageId { ChangePasswordSuccess, SetPasswordSuccess, RemoveLoginSuccess, Error } private ActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } else { return RedirectToAction("Index", "Home"); } } private class ChallengeResult : HttpUnauthorizedResult { public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null) { } public ChallengeResult(string provider, string redirectUri, string userId) { LoginProvider = provider; RedirectUri = redirectUri; UserId = userId; } public string LoginProvider { get; set; } public string RedirectUri { get; set; } public string UserId { get; set; } public override void ExecuteResult(ControllerContext context) { var properties = new AuthenticationProperties() { RedirectUri = RedirectUri }; if (UserId != null) { properties.Dictionary[XsrfKey] = UserId; } context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider); } } private static void SendResetPasswordLink(string userId, string userName, string email, EnVisageEntities db) { var request = new PasswordResetRequest { Token = Guid.NewGuid(), Id = Guid.NewGuid(), UserId = userId, ValidUntil = DateTime.Now.AddDays(Settings.Default.ForgotPasswordLinkTTLDays) }; db.PasswordResetRequests.Add(request); db.SaveChanges(); MailManager.SendRestorePasswordMessage(email, request.Token, userName); } #endregion } }