using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security.Principal; using System.Web; using System.Web.Mvc; using System.Web.Security; using System.Web.UI; using NHibernate; using NHibernate.Linq; using Sleis.Models; using Sleis.ViewModels; using Sleis.Infrastructure; using log4net; using Sleis.Utility; using Sleis.Validation.Attribute; using Sleis.Service; using System.Text.RegularExpressions; namespace Sleis.Controllers { [HandleError] public class UserController : BaseController { public string UserCreateConfirmationMessage { get; set; } public string ProfileUpdatedConfirmationMessage { get; set; } public string YourProfileUpdatedConfirmationMessage { get; set; } public string LastFacilityAdministratorErrorMessage { get; set; } private int _securityQuestionAnswerMinLength = 5; public int SecurityQuestionAnswerMinLength { set { if (value > 5) _securityQuestionAnswerMinLength = value; } get { return _securityQuestionAnswerMinLength; } } private Regex _phoneNumberRegex = new Regex(@"^([\(]{1}[2-9]{1}[0-9]{2}[\)]{1}[ ]{1}[1-9]{1}[0-9]{2}[\-]{1}[0-9]{4})$", RegexOptions.Compiled); private Regex _emailRegex = new Regex(@"^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$", RegexOptions.Compiled); //Based off of RFC 2822 - doesn't support quoted or bracketed addresses (which are extremely rare). public override void Init() { base.Init(); ArgumentValidationUtility.ThrowOnEmpty(UserCreateConfirmationMessage, "UserCreateConfirmationMessage"); ArgumentValidationUtility.ThrowOnEmpty(ProfileUpdatedConfirmationMessage, "ProfileUpdatedConfirmationMessage"); ArgumentValidationUtility.ThrowOnEmpty(YourProfileUpdatedConfirmationMessage, "YourProfileUpdatedConfirmationMessage"); ArgumentValidationUtility.ThrowOnEmpty(LastFacilityAdministratorErrorMessage, "LastFacilityAdministratorErrorMessage"); } private void LoadProfileLookups(UserModel profileUser) { ViewData[Lists.SleisSecQuestions] = UserService.UserSecQuestionList; int questionCounter = 1; while (profileUser.ContactSecAnswers.Count < UserService.UserSecQuestionListSize) { UserAnswerModel answer = new UserAnswerModel(); answer.Answer = String.Empty; answer.Question = String.Empty; answer.Index = questionCounter++; profileUser.ContactSecAnswers.Add(answer); } if (profileUser.ContactPoints.Count < 1) { UserContactModel contact = new UserContactModel(); contact.Index = questionCounter++; contact.Type = ContactMethodType.Other; contact.Value = String.Empty; contact.UserId = profileUser.Id; profileUser.ContactPoints.Add(contact); } } [Authorize] public ActionResult List() { return View(); } [Authorize] public ActionResult GetAgencyUsers() { ListView model = new ListView(); model.List = UserService.GetAgencyUsers(); return Json(model, JsonRequestBehavior.AllowGet); } [Authorize] public ActionResult GetFacilityUsers() { ListView model = new ListView(); model.List = UserService.GetFacilityUsers(); return Json(model, JsonRequestBehavior.AllowGet); } [Authorize] public ActionResult Profile() { //LoadProfileLookups(AppUser); Not sure if this is really necessary if (Request.UrlReferrer != null) { TempData[Constants.ReturnUrlKey] = Request.UrlReferrer.AbsoluteUri; SessionUtility.SetSessionVar(Constants.ReturnUrlKey, Request.UrlReferrer.AbsoluteUri); } LoadProfileLookups(AppUser.User); return View(AppUser.User); } [Authorize, HttpPost] public ActionResult Profile(UserModel profileUser, string returnUrl) { ActionResult resultView = null; //UserModel user = AppUser.User; UserModel user = UserService.GetUser(profileUser.Email, 0); TryUpdateModel(user); //update changed values UserContactModel contact; for(int i = 0; i < user.ContactPoints.Count; ++i) { contact = user.ContactPoints[i]; switch(contact.Type) { case ContactMethodType.Fax: case ContactMethodType.Mobile: case ContactMethodType.Phone: if (!String.IsNullOrWhiteSpace(contact.Value) && !_phoneNumberRegex.IsMatch(contact.Value)) { ModelState.AddModelError("ContactPoints[" + i + "].Value", "Contact Phone/Fax/Mobile format must be (###) ###-####"); } break; case ContactMethodType.Email: if (!String.IsNullOrWhiteSpace(contact.Value) && !_emailRegex.IsMatch(contact.Value)) { ModelState.AddModelError("ContactPoints[" + i + "].Value", "Contact Email format must be [user]@[domain]"); } break; } } for (int i = 0; i < user.ContactSecAnswers.Count; i++) { if (!String.IsNullOrWhiteSpace(user.ContactSecAnswers[i].Answer) && user.ContactSecAnswers[i].Answer.Length < SecurityQuestionAnswerMinLength) { ModelState.AddModelError("ContactSecAnswers[" + i + "].Answer", "Electronic Signature Challenge Question Answers must be " + SecurityQuestionAnswerMinLength + " characters or more."); user.InvalidateSecurityQuestions(); } if (user.ContactSecAnswers.Count(x => x.Answer == user.ContactSecAnswers[i].Answer) > 1) { ModelState.AddModelError("ContactSecAnswers[" + i + "].Answer", "Electronic Signature Challenge Question Answers must be unique in your profile."); user.InvalidateSecurityQuestions(); } } if (ModelState.IsValid) { UserService.UpdateUserProfile(AppUser.User, user); //Reload context to make sure we are up to date. HttpContext.Session[UserModel.SESSION_USER_KEY] = ReloadUserContext(AppUser.User.Id); TempData[Constants.GlobalMessageKey] = new SimpleMessage(ProfileUpdatedConfirmationMessage); if (String.IsNullOrEmpty(returnUrl)) { resultView = RedirectToAction("Home", "Public"); } else { resultView = new RedirectResult(returnUrl); } SessionUtility.SetSessionVar(Constants.ReturnUrlKey, null); //remove returnUrl } else { //string returnUrl = SessionUtility.Get(Constants.ReturnUrlKey); if (!string.IsNullOrEmpty(returnUrl)) { TempData[Constants.ReturnUrlKey] = returnUrl; } LoadProfileLookups(user); resultView = View(user); } return resultView; } [Authorize, AppRoleValidation(AppUserRoleType.AgencyAdmin)] public ActionResult Create(string type) { UserModel user = new UserModel(); user.Type = (SleisUserTypeType)Enum.Parse(typeof(SleisUserTypeType), type, true); user.Status = SleisUserStatusType.Active; return SetLookups(user); } [HttpPost, Authorize, AppRoleValidation(AppUserRoleType.AgencyAdmin)] public ActionResult Create(UserModel user, int? facilityId) { ActionResult resultView = null; if (ModelState.IsValid) { if (UserService.UserData.EmailExists(user.Email)) { ModelState.AddModelError("Email", "Email already registered"); resultView = SetLookups(user); } else { //Create empty account (no password) UserService.CreateUser(user, facilityId, String.Format("{0}/Account/Verify/", BaseUri)); resultView = RedirectToAction("Home", "Public"); } } else { resultView = SetLookups(user); } return resultView; } [Authorize] public ActionResult ViewUser(int id, int facilityId=0) { SetSelectedFacility(facilityId); UserModel user = GetFacilityUser(id, facilityId); TempData["FilterBy"] = user.Type.ToString(); return View(user); } [Authorize, AppRoleValidation(AppUserRoleType.FacilityAdmin, AppUserRoleType.AgencyAdmin)] public ActionResult DeleteUserRole(int id, int facilityId=0) { try { UserService.DeleteUserRoles(id, facilityId); TempData[Constants.GlobalMessageKey] = new SimpleMessage("Deleting user roles successfully"); } catch (Exception ex) { Log.Error("Delete User Roles", ex); TempData[Constants.GlobalMessageKey] = new SimpleMessage("Error in deleting user roles"); } return RedirectToAction("FacilityUser", "Facility", new { id = facilityId }); } private void ValidateContacts(ModelStateDictionary modelState, ManageUserView profileUser) { UserContactModel contact; for (int i = 0; i < profileUser.User.ContactPoints.Count; ++i) { contact = profileUser.User.ContactPoints[i]; switch (contact.Type) { case ContactMethodType.Fax: case ContactMethodType.Mobile: case ContactMethodType.Phone: if (!String.IsNullOrWhiteSpace(contact.Value) && !_phoneNumberRegex.IsMatch(contact.Value)) { modelState.AddModelError("User.ContactPoints[" + i + "].Value", "Contact Phone/Fax/Mobile format must be (###) ###-####"); } break; case ContactMethodType.Email: if (!String.IsNullOrWhiteSpace(contact.Value) && !_emailRegex.IsMatch(contact.Value)) { modelState.AddModelError("User.ContactPoints[" + i + "].Value", "Contact Email format must be [user]@[domain]"); } break; } } } [Authorize, HttpGet, AppRoleValidation(AppUserRoleType.AgencyAdmin)] public ActionResult Add(string type, int? facilityId) { if (facilityId.HasValue) { SetSelectedFacility(facilityId.Value); } ManageUserView model = new ManageUserView(); model.User = new UserModel(); model.User.Status = SleisUserStatusType.Active; model.User.Type = (SleisUserTypeType)Enum.Parse(typeof(SleisUserTypeType), type, true); model.User.ContactPoints.Add(new UserContactModel()); //manually create blank row. model.MainRoles = new List(); model.SubRoles = new List(); return View("ManageUser", model); } [Authorize, HttpPost, AppRoleValidation(AppUserRoleType.AgencyAdmin)] public ActionResult Add(ManageUserView profileUser, int? facilityId) { ActionResult resultView = null; TempData["FilterBy"] = profileUser.User.Type.ToString(); ValidateContacts(ModelState, profileUser); if (!String.IsNullOrWhiteSpace(profileUser.User.Email) && UserService.UserData.EmailExists(profileUser.User.Email)) { ModelState.AddModelError("User.Email", "Email already registered."); } if (ModelState.IsValid) { //Add/merge appropriate roles to user profileUser.User.Roles = new List(); if (profileUser.MainRoles != null) profileUser.User.Roles.AddRange(profileUser.MainRoles); if (profileUser.SubRoles != null) profileUser.User.Roles.AddRange(profileUser.SubRoles); //Create empty account (no password) UserService.CreateUser(profileUser.User, facilityId, String.Format("{0}/Account/Verify/", BaseUri)); UserView currentUser = (UserView)HttpContext.Session[UserModel.SESSION_USER_KEY]; TempData[Constants.GlobalMessageKey] = new SimpleMessage(UserCreateConfirmationMessage); TempData["FilterBy"] = profileUser.User.Type.ToString(); if (facilityId > 0) { resultView = RedirectToAction("FacilityUser", "Facility", new { id = facilityId }); } else { resultView = RedirectToAction("List", "User"); } } else { resultView = View("ManageUser", profileUser); } return resultView; } [Authorize, AppRoleValidation(AppUserRoleType.FacilityAdmin, AppUserRoleType.AgencyAdmin)] public ActionResult ManageUser(int id, int facilityId=0) { ActionResult resultView = null; if (Request.UrlReferrer != null) { TempData[Constants.ReturnUrlKey] = Request.UrlReferrer.AbsoluteUri; } SetSelectedFacility(facilityId); ManageUserView muv = new ManageUserView(); muv.User = UserService.GetUser(id, facilityId); muv.MainRoles = muv.User.Roles.Where(r => r == AppUserRoleType.FacilityViewer || r == AppUserRoleType.FacilityEditor || r==AppUserRoleType.AgencyViewer || r==AppUserRoleType.AgencyEditor).ToList(); muv.SubRoles = muv.User.Roles.Where(r => r == AppUserRoleType.FacilitySubmitter || r == AppUserRoleType.FacilityAdmin || r==AppUserRoleType.AgencySubmitter || r==AppUserRoleType.AgencyAdmin || r==AppUserRoleType.AgencyAcceptor).ToList(); TempData["FilterBy"] = muv.User.Type.ToString().ToLower(); resultView = View(muv); return resultView; } [Authorize, HttpPost, AppRoleValidation(AppUserRoleType.FacilityAdmin, AppUserRoleType.AgencyAdmin)] public ActionResult ManageUser(ManageUserView profileUser, int facilityId=0) { ActionResult resultView = null; TempData["FilterBy"] = profileUser.User.Type.ToString(); ValidateContacts(ModelState, profileUser); if (ModelState.IsValid) { //Add/merge appropriate roles to user profileUser.User.Roles = new List(); if(profileUser.MainRoles != null) profileUser.User.Roles.AddRange(profileUser.MainRoles); if(profileUser.SubRoles != null) profileUser.User.Roles.AddRange(profileUser.SubRoles); UserModel result = UserService.UpdateFacilityUser(profileUser.User, facilityId); if (result == null) { TempData[Constants.GlobalMessageKey] = new SimpleMessage(LastFacilityAdministratorErrorMessage, true); return View(profileUser); } UserView currentUser = (UserView)HttpContext.Session[UserModel.SESSION_USER_KEY]; if (currentUser.User.Email == profileUser.User.Email) { TempData[Constants.GlobalMessageKey] = new SimpleMessage(YourProfileUpdatedConfirmationMessage); } else { TempData[Constants.GlobalMessageKey] = new SimpleMessage(ProfileUpdatedConfirmationMessage); } string returnUrl = TempData[Constants.ReturnUrlKey] as string; if (String.IsNullOrEmpty(returnUrl)) { resultView = RedirectToAction("Home", "Public"); } else { resultView = new RedirectResult(returnUrl); } } else { resultView = View(profileUser); } return resultView; } [HttpGet, Authorize, AppRoleValidation(AppUserRoleType.AgencyAdmin)] public ActionResult Delete(int id) { UserModel user = UserService.GetUser(id); TempData["FilterBy"] = user.Type.ToString(); UserService.DeleteUser(id); TempData[Constants.GlobalMessageKey] = new SimpleMessage("User has been successfully deleted."); return RedirectToAction("List"); } [HttpGet, Authorize, AppRoleValidation(AppUserRoleType.AgencyAdmin)] public ActionResult LockAccount(int id) { UserModel user = UserService.GetUser(id); TempData["FilterBy"] = user.Type.ToString(); UserService.UpdateUserStatus(id, SleisUserStatusType.Locked); TempData[Constants.GlobalMessageKey] = new SimpleMessage("User's account has successfully been locked."); return RedirectToAction("List"); } [HttpGet, Authorize, AppRoleValidation(AppUserRoleType.AgencyAdmin)] public ActionResult UnlockAccount(int id) { UserModel user = UserService.GetUser(id); TempData["FilterBy"] = user.Type.ToString(); UserService.UpdateUserStatus(id, SleisUserStatusType.Active); //reset users password UserService.RetirePassword(user); TempData[Constants.GlobalMessageKey] = new SimpleMessage("User's account has successfully been unlocked."); return RedirectToAction("List"); } [HttpGet, Authorize, AppRoleValidation(AppUserRoleType.AgencyAdmin)] public ActionResult DeactivateAccount(int id) { UserModel user = UserService.GetUser(id); TempData["FilterBy"] = user.Type.ToString(); UserService.UpdateUserStatus(id, SleisUserStatusType.Inactive); TempData[Constants.GlobalMessageKey] = new SimpleMessage("User's account has successfully been deactivated."); return RedirectToAction("List"); } [HttpGet, Authorize, AppRoleValidation(AppUserRoleType.AgencyAdmin)] public ActionResult ResetPassword(int id) { UserModel user = UserService.GetUser(id); TempData["FilterBy"] = user.Type.ToString(); //retire previous password. UserService.RetirePassword(user); //email user link to reset their password UserService.Forgot(user.Email, String.Format("{0}/Account/Verify/", BaseUri)); TempData[Constants.GlobalMessageKey] = new SimpleMessage("User's password has successfully been reset."); return RedirectToAction("List"); } [HttpGet, Authorize, AppRoleValidation(AppUserRoleType.AgencyAdmin)] public ActionResult ResetSecurityQuestions(int id) { UserModel user = UserService.GetUser(id); TempData["FilterBy"] = user.Type.ToString(); UserService.ResetSecurityQuestions(user); TempData[Constants.GlobalMessageKey] = new SimpleMessage("User's security questions have successfully been reset."); return RedirectToAction("List"); } [HttpGet, Authorize] public ActionResult Remove(int id, int facilityId) { UserModel user = UserService.GetUser(id); TempData["FilterBy"] = user.Type.ToString(); UserService.DeleteUserRoles(id, facilityId); TempData[Constants.GlobalMessageKey] = new SimpleMessage("User has been successfully removed from the facility."); return RedirectToAction("FacilityUser", "Facility", new { id = facilityId }); } private ActionResult SetLookups(UserModel user) { return View(user); } private UserModel GetFacilityUser(int userId, int facilityId=0) { UserModel user = UserService.UserData.GetById(userId); user.Roles = UserService.RoleData.GetFacilityRoles(userId, facilityId); user.ContactPoints = UserService.UserContactData.GetByUserId(userId); LoadProfileLookups(user); return user; } } }