diff --git a/Web.Tests/Membership/CryptoTests.cs b/Web.Tests/Utility/CryptoTests.cs similarity index 93% rename from Web.Tests/Membership/CryptoTests.cs rename to Web.Tests/Utility/CryptoTests.cs index d34ff2c..cbd676f 100644 --- a/Web.Tests/Membership/CryptoTests.cs +++ b/Web.Tests/Utility/CryptoTests.cs @@ -1,4 +1,4 @@ -using MileageTraker.Web.Membership; +using MileageTraker.Web.Utility; using NUnit.Framework; namespace Web.Tests.Membership diff --git a/Web.Tests/ViewModels/CreateUserViewModelTests.cs b/Web.Tests/ViewModels/CreateUserViewModelTests.cs index 31e6445..57adb73 100644 --- a/Web.Tests/ViewModels/CreateUserViewModelTests.cs +++ b/Web.Tests/ViewModels/CreateUserViewModelTests.cs @@ -12,14 +12,12 @@ namespace Web.Tests.ViewModels var vm = new CreateUserViewModel(); var email = vm.Email = "bob@dobalina.com"; - var firstName = vm.FirstName = "bob"; - var lastName = vm.LastName = "dobalina"; + var userName = vm.Username = "bob dobalina"; - var user = vm.CloneToUser(); + var user = vm.ToUser(); Assert.That(user.Email, Is.EqualTo(email)); - Assert.That(user.FirstName, Is.EqualTo(firstName)); - Assert.That(user.LastName, Is.EqualTo(lastName)); + Assert.That(user.Username, Is.EqualTo(userName)); } } } diff --git a/Web.Tests/Web.Tests.csproj b/Web.Tests/Web.Tests.csproj index c14f439..0d94bf3 100644 --- a/Web.Tests/Web.Tests.csproj +++ b/Web.Tests/Web.Tests.csproj @@ -84,7 +84,7 @@ - + diff --git a/Web.Tests/app.config b/Web.Tests/app.config index 56f696d..e72c720 100644 --- a/Web.Tests/app.config +++ b/Web.Tests/app.config @@ -6,6 +6,10 @@ + + + + \ No newline at end of file diff --git a/Web/Attributes/UserActivityAttribute.cs b/Web/Attributes/UserActivityAttribute.cs new file mode 100644 index 0000000..d6929b0 --- /dev/null +++ b/Web/Attributes/UserActivityAttribute.cs @@ -0,0 +1,20 @@ +using System.Web.Mvc; +using MileageTraker.Web.DAL; + +namespace MileageTraker.Web.Attributes +{ + public class UserActivityAttribute : ActionFilterAttribute + { + public override void OnActionExecuting(ActionExecutingContext filterContext) + { + if (filterContext != null) + { + using (var ds = new DataService()) + { + ds.UpdateUserLastActivity(filterContext.HttpContext.User.Identity.Name); + } + } + base.OnActionExecuting(filterContext); + } + } +} \ No newline at end of file diff --git a/Web/Content/Site.css b/Web/Content/Site.css index 35132b8..438e544 100644 --- a/Web/Content/Site.css +++ b/Web/Content/Site.css @@ -3,6 +3,20 @@ body { padding-top: 60px; } +#content > form { + max-width: 400px; + padding: 10px; + margin: 0 auto 20px; + background-color: whitesmoke; + border: 1px solid #e5e5e5; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: 0 1px 2px rgba(0,0,0,.05); + box-shadow: 0 1px 2px rgba(0,0,0,.05); +} + .brand { background: url(images/Header.png) no-repeat left center; @@ -42,6 +56,23 @@ body { margin: 0; } +.breadcrumb { + display: inline-block; + margin-bottom: 10px; +} + +.form-horizontal .control-label { + width: 140px; +} + +.form-horizontal .controls { + margin-left: 160px; +} + +.form-horizontal .control-group { + margin-bottom: 10px; +} + footer { background-color: #101010; color: #666; @@ -137,10 +168,17 @@ dl.inline { padding: 0; } - .container-fluid { + #content { padding: 0 10px; } + .navbar .container-fluid { + padding: 0 10px; + } + + .navbar .brand { + } + .input-mini { width: 60px !important; } @@ -166,20 +204,21 @@ dl.inline { padding: 0; } - .container-fluid { + #content { padding: 0 10px; } - .brand { background: url(images/Header.mobile.png) no-repeat left center; height: 28px; width: 205px; } + .navbar .container-fluid { + padding: 0 10px; + } + .navbar .brand { - margin-left: 5px; - padding: 10px 0; } fieldset legend { @@ -200,9 +239,9 @@ dl.inline { margin-bottom: 10px; } - .form-horizontal .control-group.endOdometer { - margin-bottom: 0; - } + .form-horizontal .control-group.endOdometer { + margin-bottom: 0; + } .employeeName input { width: 120px; diff --git a/Web/Controllers/AccountController.cs b/Web/Controllers/AccountController.cs index 5b3fd5b..7826cb6 100644 --- a/Web/Controllers/AccountController.cs +++ b/Web/Controllers/AccountController.cs @@ -1,7 +1,6 @@ using System; using System.Web.Mvc; using System.Web.Security; -using MileageTraker.Web.Membership; using MileageTraker.Web.ViewModels.Account; namespace MileageTraker.Web.Controllers @@ -21,10 +20,15 @@ namespace MileageTraker.Web.Controllers [ValidateAntiForgeryToken] public ActionResult Login(LoginViewModel model, string returnUrl) { - if (ModelState.IsValid && WebSecurity.Login(model.EmailAddress, model.Password, model.RememberMe)) + if (ModelState.IsValid) { - // TODO: send notification to user - return RedirectToLocal(returnUrl); + var success = Membership.ValidateUser(model.Username, model.Password); + if (success) + { + FormsAuthentication.SetAuthCookie(model.Username, model.RememberMe); + // TODO: send notification to user + return RedirectToLocal(returnUrl); + } } // If we got this far, something failed, redisplay form @@ -36,48 +40,47 @@ namespace MileageTraker.Web.Controllers [ValidateAntiForgeryToken] public ActionResult LogOff() { - WebSecurity.Logout(); + FormsAuthentication.SignOut(); // TODO: send notification to user return RedirectToAction("Index", "CreateLog"); } - [AllowAnonymous] - public ActionResult Register() - { - return View(); - } + //[AllowAnonymous] + //public ActionResult Register() + //{ + // return View(); + //} - [HttpPost] - [AllowAnonymous] - [ValidateAntiForgeryToken] - public ActionResult Register(RegisterModel model) - { - if (ModelState.IsValid) - { - // Attempt to register the user - try - { - WebSecurity.CreateUserAndAccount(model.UserName, model.Password); - WebSecurity.Login(model.UserName, model.Password); - return RedirectToAction("Login"); - } - catch (MembershipCreateUserException e) - { - ModelState.AddModelError("", ErrorCodeToString(e.StatusCode)); - } - } + //[HttpPost] + //[AllowAnonymous] + //[ValidateAntiForgeryToken] + //public ActionResult Register(RegisterModel model) + //{ + // if (ModelState.IsValid) + // { + // // Attempt to register the user + // try + // { + // WebSecurity.CreateUserAndAccount(model.UserName, model.Password); + // WebSecurity.Login(model.UserName, model.Password); + // return RedirectToAction("Login"); + // } + // catch (MembershipCreateUserException e) + // { + // ModelState.AddModelError("", ErrorCodeToString(e.StatusCode)); + // } + // } - // If we got this far, something failed, redisplay form - return View(model); - } + // // If we got this far, something failed, redisplay form + // return View(model); + //} 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." : ""; ViewBag.ReturnUrl = Url.Action("Manage"); return View(); @@ -95,9 +98,10 @@ namespace MileageTraker.Web.Controllers bool changePasswordSucceeded; try { - changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword); + var currentUser = Membership.GetUser(User.Identity.Name, true); + changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword); } - catch (Exception ex) + catch (Exception) { changePasswordSucceeded = false; } @@ -106,10 +110,7 @@ namespace MileageTraker.Web.Controllers { return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess }); } - else - { - ModelState.AddModelError("", "The current password is incorrect or the new password is invalid."); - } + ModelState.AddModelError("", "The current password is incorrect or the new password is invalid."); } // If we got this far, something failed, redisplay form @@ -129,7 +130,6 @@ namespace MileageTraker.Web.Controllers { ChangePasswordSuccess, SetPasswordSuccess, - RemoveLoginSuccess, } private static string ErrorCodeToString(MembershipCreateStatus createStatus) diff --git a/Web/Controllers/UserController.cs b/Web/Controllers/UserController.cs index 13adb36..9385ab4 100644 --- a/Web/Controllers/UserController.cs +++ b/Web/Controllers/UserController.cs @@ -1,14 +1,15 @@ using System; -using System.Data; using System.Linq; using System.Web.Mvc; +using System.Web.Security; +using MileageTraker.Web.Attributes; using MileageTraker.Web.Models; -using MileageTraker.Web.Context; using MileageTraker.Web.ViewModels.User; namespace MileageTraker.Web.Controllers { [Authorize(Roles = "Administrator, Developer")] + [UserActivity] public class UserController : ControllerBase { public ActionResult Index() @@ -18,32 +19,66 @@ namespace MileageTraker.Web.Controllers public ActionResult Details(Guid id) { - var user = DataService.GetUser(id); + var user = Membership.GetUser(id); if (user == null) { return HttpNotFound(); } - return View(user); + return View(DataService.GetUser(id)); } public ActionResult Create() { - return View(); + var vm = new CreateUserViewModel + { + AvailableRoles = Roles.GetAllRoles() + }; + + return View(vm); } [HttpPost] - public ActionResult Create(CreateUserViewModel createUserViewModel) + public ActionResult Create(CreateUserViewModel viewModel) { if (ModelState.IsValid) { - var user = createUserViewModel.CloneToUser(); - user.UserId = Guid.NewGuid(); - //db.Users.Add(user); - //db.SaveChanges(); - return RedirectToAction("Index"); + var hasRoles = viewModel.Roles != null && viewModel.Roles.Any(); + + MembershipCreateStatus membershipCreateStatus; + var membershipUser = + Membership.CreateUser( + viewModel.Username, + viewModel.Password, + viewModel.Email, + null, + null, + hasRoles, + out membershipCreateStatus); + + if (membershipUser == null) + { + ModelState.AddModelError("", ErrorCodeToString(membershipCreateStatus)); + viewModel.AvailableRoles = Roles.GetAllRoles(); + return View(viewModel); + } + + if (hasRoles) + { + Roles.AddUserToRoles( + membershipUser.UserName, + viewModel.Roles); + } + + var user = DataService.GetUser( + (Guid) membershipUser.ProviderUserKey); + user.FullName = viewModel.FullName; + DataService.UpdateUserPersonalInfo(user); + + return RedirectToAction("Index"); } - return View(createUserViewModel); + viewModel.AvailableRoles = Roles.GetAllRoles(); + return View(viewModel); } public ActionResult Edit(Guid id) @@ -61,11 +96,48 @@ namespace MileageTraker.Web.Controllers { if (ModelState.IsValid) { - //db.Entry(user).State = EntityState.Modified; - //db.SaveChanges(); + DataService.UpdateUser(user); return RedirectToAction("Index"); } return View(user); } + + private static string ErrorCodeToString(MembershipCreateStatus createStatus) + { + // See http://go.microsoft.com/fwlink/?LinkID=177550 for + // a full list of status codes. + switch (createStatus) + { + case MembershipCreateStatus.DuplicateUserName: + return "User name already exists. Please enter a different user name."; + + case MembershipCreateStatus.DuplicateEmail: + return "A user name for that e-mail address already exists. Please enter a different e-mail address."; + + case MembershipCreateStatus.InvalidPassword: + return "The password provided is invalid. Please enter a valid password value."; + + case MembershipCreateStatus.InvalidEmail: + return "The e-mail address provided is invalid. Please check the value and try again."; + + case MembershipCreateStatus.InvalidAnswer: + return "The password retrieval answer provided is invalid. Please check the value and try again."; + + case MembershipCreateStatus.InvalidQuestion: + return "The password retrieval question provided is invalid. Please check the value and try again."; + + case MembershipCreateStatus.InvalidUserName: + return "The user name provided is invalid. Please check the value and try again."; + + case MembershipCreateStatus.ProviderError: + return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator."; + + case MembershipCreateStatus.UserRejected: + return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator."; + + default: + return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator."; + } + } } } \ No newline at end of file diff --git a/Web/Membership/CodeFirstMembershipProvider.cs b/Web/DAL/CodeFirstMembershipProvider.cs similarity index 56% rename from Web/Membership/CodeFirstMembershipProvider.cs rename to Web/DAL/CodeFirstMembershipProvider.cs index fc619bd..8f3e9be 100644 --- a/Web/Membership/CodeFirstMembershipProvider.cs +++ b/Web/DAL/CodeFirstMembershipProvider.cs @@ -1,21 +1,15 @@ using System; -using System.Data.SqlTypes; using System.Linq; -using System.Security.Cryptography; -using System.Web; using System.Web.Security; -using MileageTraker.Web.Context; using MileageTraker.Web.Models; using MileageTraker.Web.Utility; -namespace MileageTraker.Web.Membership +namespace MileageTraker.Web.DAL { public class CodeFirstMembershipProvider : MembershipProvider { #region Properties - private const int TokenSizeInBytes = 16; - public override string ApplicationName { get { return GetType().Assembly.GetName().Name; } @@ -92,15 +86,15 @@ namespace MileageTraker.Web.Membership return null; } - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - if (context.Users.Any(usr => usr.Username == username)) + if (context.FindUserByUsername(username) != null) { status = MembershipCreateStatus.DuplicateUserName; return null; } - if (context.Users.Any(usr => usr.Email == email)) + if (context.FindUserByUsername(email) != null) { status = MembershipCreateStatus.DuplicateEmail; return null; @@ -113,28 +107,16 @@ namespace MileageTraker.Web.Membership Password = hashedPassword, IsApproved = isApproved, Email = email, - CreateDate = DateTime.UtcNow, - LastPasswordChangedDate = DateTime.UtcNow, - PasswordFailuresSinceLastSuccess = 0, - LastLoginDate = DateTime.UtcNow, - LastActivityDate = DateTime.UtcNow, - LastLockoutDate = SqlDateTime.MinValue.Value, - IsLockedOut = false, - LastPasswordFailureDate = SqlDateTime.MinValue.Value + Created = DateTime.Now, + FullName = "Temp Name" }; - context.Users.Add(newUser); - context.SaveChanges(); + context.AddUser(newUser); status = MembershipCreateStatus.Success; - return newUser.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name); + return newUser.ToMembershipUser(Membership.Provider.Name); } } - - public string CreateUserAndAccount(string userName, string password, bool requireConfirmation) - { - return CreateAccount(userName, password, requireConfirmation); - } - + public override bool ValidateUser(string username, string password) { if (string.IsNullOrEmpty(username)) @@ -145,9 +127,9 @@ namespace MileageTraker.Web.Membership { return false; } - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - User user = context.Users.FirstOrDefault(usr => usr.Username == username); + var user = context.FindUserByUsername(username); if (user == null) { return false; @@ -165,8 +147,8 @@ namespace MileageTraker.Web.Membership if (verificationSucceeded) { user.PasswordFailuresSinceLastSuccess = 0; - user.LastLoginDate = DateTime.UtcNow; - user.LastActivityDate = DateTime.UtcNow; + user.LastLoginDate = DateTime.Now; + user.LastActivityDate = DateTime.Now; } else { @@ -174,16 +156,16 @@ namespace MileageTraker.Web.Membership if (failures < MaxInvalidPasswordAttempts) { user.PasswordFailuresSinceLastSuccess += 1; - user.LastPasswordFailureDate = DateTime.UtcNow; + user.LastPasswordFailureDate = DateTime.Now; } else if (failures >= MaxInvalidPasswordAttempts) { - user.LastPasswordFailureDate = DateTime.UtcNow; - user.LastLockoutDate = DateTime.UtcNow; + user.LastPasswordFailureDate = DateTime.Now; + user.LastLockoutDate = DateTime.Now; user.IsLockedOut = true; } } - context.SaveChanges(); + context.UpdateUser(user); return verificationSucceeded; } } @@ -194,9 +176,9 @@ namespace MileageTraker.Web.Membership { return null; } - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - var user = context.Users.FirstOrDefault(usr => usr.Username == username); + var user = context.FindUserByUsername(username); if (user == null) { return null; @@ -204,26 +186,23 @@ namespace MileageTraker.Web.Membership if (userIsOnline) { - user.LastActivityDate = DateTime.UtcNow; - context.SaveChanges(); + user.LastActivityDate = DateTime.Now; + context.UpdateUser(user); } - return user.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name); + return user.ToMembershipUser(System.Web.Security.Membership.Provider.Name); } } public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) { - if (providerUserKey is Guid) - { - } - else + if (!(providerUserKey is Guid)) { return null; } - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - var user = context.Users.Find(providerUserKey); + var user = context.GetUser((Guid)providerUserKey); if (user == null) { return null; @@ -231,20 +210,20 @@ namespace MileageTraker.Web.Membership if (userIsOnline) { - user.LastActivityDate = DateTime.UtcNow; - context.SaveChanges(); + user.LastActivityDate = DateTime.Now; + context.UpdateUser(user); } - return user.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name); + return user.ToMembershipUser(System.Web.Security.Membership.Provider.Name); } } - public override bool ChangePassword(string username, string oldPassword, string newPassword) + public override bool ChangePassword(string username, string currentPassword, string newPassword) { if (string.IsNullOrEmpty(username)) { return false; } - if (string.IsNullOrEmpty(oldPassword)) + if (string.IsNullOrEmpty(currentPassword)) { return false; } @@ -252,15 +231,15 @@ namespace MileageTraker.Web.Membership { return false; } - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - User user = context.Users.FirstOrDefault(usr => usr.Username == username); + var user = context.FindUserByUsername(username); if (user == null) { return false; } var hashedPassword = user.Password; - var verificationSucceeded = (hashedPassword != null && Crypto.VerifyHashedPassword(hashedPassword, oldPassword)); + var verificationSucceeded = (hashedPassword != null && Crypto.VerifyHashedPassword(hashedPassword, currentPassword)); if (verificationSucceeded) { user.PasswordFailuresSinceLastSuccess = 0; @@ -271,15 +250,15 @@ namespace MileageTraker.Web.Membership if (failures < MaxInvalidPasswordAttempts) { user.PasswordFailuresSinceLastSuccess += 1; - user.LastPasswordFailureDate = DateTime.UtcNow; + user.LastPasswordFailureDate = DateTime.Now; } else if (failures >= MaxInvalidPasswordAttempts) { - user.LastPasswordFailureDate = DateTime.UtcNow; - user.LastLockoutDate = DateTime.UtcNow; + user.LastPasswordFailureDate = DateTime.Now; + user.LastLockoutDate = DateTime.Now; user.IsLockedOut = true; } - context.SaveChanges(); + context.UpdateUser(user); return false; } var newHashedPassword = Crypto.HashPassword(newPassword); @@ -288,22 +267,22 @@ namespace MileageTraker.Web.Membership return false; } user.Password = newHashedPassword; - user.LastPasswordChangedDate = DateTime.UtcNow; - context.SaveChanges(); + user.LastPasswordChangedDate = DateTime.Now; + context.UpdateUser(user); return true; } } public override bool UnlockUser(string userName) { - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - var user = context.Users.FirstOrDefault(usr => usr.Username == userName); + var user = context.FindUserByUsername(userName); if (user != null) { user.IsLockedOut = false; user.PasswordFailuresSinceLastSuccess = 0; - context.SaveChanges(); + context.UpdateUser(user); return true; } return false; @@ -313,36 +292,22 @@ namespace MileageTraker.Web.Membership public override int GetNumberOfUsersOnline() { var dateActive = CustomExtensions.UserOnlineThreshold(); - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - return context.Users.Count(usr => usr.LastActivityDate > dateActive); + return context.GetUsers().Count(usr => usr.LastActivityDate > dateActive); } } public override bool DeleteUser(string username, bool deleteAllRelatedData) { - if (string.IsNullOrEmpty(username)) - { - return false; - } - using (var context = new MileageTrakerContext()) - { - var user = context.Users.FirstOrDefault(usr => usr.Username == username); - if (user != null) - { - context.Users.Remove(user); - context.SaveChanges(); - return true; - } - return false; - } + throw new NotSupportedException(); } public override string GetUserNameByEmail(string email) { - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - var user = context.Users.FirstOrDefault(usr => usr.Email == email); + var user = context.FindUserByEmail(email); if (user != null) { return user.Username; @@ -355,18 +320,18 @@ namespace MileageTraker.Web.Membership out int totalRecords) { var membershipUsers = new MembershipUserCollection(); - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - totalRecords = context.Users.Count(user => user.Email == emailToMatch); + totalRecords = context.GetUsers().Count(user => user.Email == emailToMatch); var users = - context.Users.Where(usr => usr.Email == emailToMatch) + context.GetUsers().Where(usr => usr.Email == emailToMatch) .OrderBy(usrn => usrn.Username) .Skip(pageIndex*pageSize) .Take(pageSize); foreach (var user in users) { membershipUsers.Add( - user.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name)); + user.ToMembershipUser(Membership.Provider.Name)); } } return membershipUsers; @@ -376,17 +341,17 @@ namespace MileageTraker.Web.Membership out int totalRecords) { var membershipUsers = new MembershipUserCollection(); - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - totalRecords = context.Users.Count(Usr => Usr.Username == usernameToMatch); + totalRecords = context.GetUsers().Count(usr => usr.Username == usernameToMatch); var users = - context.Users.Where(usr => usr.Username == usernameToMatch) + context.GetUsers().Where(usr => usr.Username == usernameToMatch) .OrderBy(usrn => usrn.Username) .Skip(pageIndex*pageSize) .Take(pageSize); foreach (var user in users) { - membershipUsers.Add(user.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name)); + membershipUsers.Add(user.ToMembershipUser(Membership.Provider.Name)); } } return membershipUsers; @@ -395,92 +360,76 @@ namespace MileageTraker.Web.Membership public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) { var membershipUsers = new MembershipUserCollection(); - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - totalRecords = context.Users.Count(); - var users = - context.Users + totalRecords = context.GetUsers().Count(); + var users = + context.GetUsers() .OrderBy(usrn => usrn.Username) .Skip(pageIndex*pageSize) .Take(pageSize); foreach (var user in users) { - membershipUsers.Add(user.CloneToMembershipUser(System.Web.Security.Membership.Provider.Name)); + membershipUsers.Add(user.ToMembershipUser(Membership.Provider.Name)); } } return membershipUsers; } - public string CreateAccount(string userName, string password, bool requireConfirmationToken) - { - if (string.IsNullOrEmpty(userName)) - { - throw new MembershipCreateUserException(MembershipCreateStatus.InvalidUserName); - } + //public string CreateAccount(string userName, string password, bool requireConfirmationToken) + //{ + // if (string.IsNullOrEmpty(userName)) + // { + // throw new MembershipCreateUserException(MembershipCreateStatus.InvalidUserName); + // } - if (string.IsNullOrEmpty(password)) - { - throw new MembershipCreateUserException(MembershipCreateStatus.InvalidPassword); - } + // if (string.IsNullOrEmpty(password)) + // { + // throw new MembershipCreateUserException(MembershipCreateStatus.InvalidPassword); + // } - var hashedPassword = Crypto.HashPassword(password); - if (hashedPassword.Length > 128) - { - throw new MembershipCreateUserException(MembershipCreateStatus.InvalidPassword); - } + // var hashedPassword = Crypto.HashPassword(password); + // if (hashedPassword.Length > 128) + // { + // throw new MembershipCreateUserException(MembershipCreateStatus.InvalidPassword); + // } - using (var context = new MileageTrakerContext()) - { - if (context.Users.Any(usr => usr.Username == userName)) - { - throw new MembershipCreateUserException(MembershipCreateStatus.DuplicateUserName); - } + // using (var context = new MileageTrakerContext()) + // { + // if (context.Users.Any(usr => usr.Username == userName)) + // { + // throw new MembershipCreateUserException(MembershipCreateStatus.DuplicateUserName); + // } - var token = string.Empty; - if (requireConfirmationToken) - { - token = GenerateToken(); - } + // var token = string.Empty; + // if (requireConfirmationToken) + // { + // token = Algorithms.GenerateToken(); + // } - var newUser = new User - { - UserId = Guid.NewGuid(), - Username = userName, - Password = hashedPassword, - IsApproved = !requireConfirmationToken, - Email = string.Empty, - CreateDate = DateTime.UtcNow, - LastPasswordChangedDate = DateTime.UtcNow, - PasswordFailuresSinceLastSuccess = 0, - LastLoginDate = DateTime.UtcNow, - LastActivityDate = DateTime.UtcNow, - LastLockoutDate = SqlDateTime.MinValue.Value, - IsLockedOut = false, - LastPasswordFailureDate = SqlDateTime.MinValue.Value, - ConfirmationToken = token - }; + // var newUser = new User + // { + // UserId = Guid.NewGuid(), + // Username = userName, + // Password = hashedPassword, + // IsApproved = !requireConfirmationToken, + // Email = string.Empty, + // Created = DateTime.Now, + // LastPasswordChangedDate = DateTime.Now, + // PasswordFailuresSinceLastSuccess = 0, + // LastLoginDate = SqlDateTime.MinValue.Value, + // LastActivityDate = SqlDateTime.MinValue.Value, + // LastLockoutDate = SqlDateTime.MinValue.Value, + // IsLockedOut = false, + // LastPasswordFailureDate = SqlDateTime.MinValue.Value + // }; - context.Users.Add(newUser); - context.SaveChanges(); - return token; - } - } - - private static string GenerateToken() - { - using (var prng = new RNGCryptoServiceProvider()) - { - return GenerateToken(prng); - } - } - - private static string GenerateToken(RandomNumberGenerator generator) - { - var tokenBytes = new byte[TokenSizeInBytes]; - generator.GetBytes(tokenBytes); - return HttpServerUtility.UrlTokenEncode(tokenBytes); - } + // context.Users.Add(newUser); + // context.SaveChanges(); + // return token; + // } + //} #endregion diff --git a/Web/Membership/CodeFirstRoleProvider.cs b/Web/DAL/CodeFirstRoleProvider.cs similarity index 91% rename from Web/Membership/CodeFirstRoleProvider.cs rename to Web/DAL/CodeFirstRoleProvider.cs index 4af432b..742eec7 100644 --- a/Web/Membership/CodeFirstRoleProvider.cs +++ b/Web/DAL/CodeFirstRoleProvider.cs @@ -4,7 +4,7 @@ using System.Web.Security; using MileageTraker.Web.Context; using MileageTraker.Web.Models; -namespace MileageTraker.Web.Membership +namespace MileageTraker.Web.DAL { public class CodeFirstRoleProvider : RoleProvider { @@ -101,6 +101,7 @@ namespace MileageTraker.Web.Membership public override string[] FindUsersInRole(string roleName, string usernameToMatch) { + throw new NotSupportedException(); if (string.IsNullOrEmpty(roleName)) { return null; @@ -122,6 +123,7 @@ namespace MileageTraker.Web.Membership public override void CreateRole(string roleName) { + throw new NotSupportedException(); if (!string.IsNullOrEmpty(roleName)) { using (var context = new MileageTrakerContext()) @@ -143,6 +145,7 @@ namespace MileageTraker.Web.Membership public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) { + throw new NotSupportedException(); if (string.IsNullOrEmpty(roleName)) { return false; @@ -173,10 +176,10 @@ namespace MileageTraker.Web.Membership public override void AddUsersToRoles(string[] usernames, string[] roleNames) { - using (var context = new MileageTrakerContext()) + using (var context = new DataService()) { - var users = context.Users.Where(usr => usernames.Contains(usr.Username)).ToList(); - var roles = context.Roles.Where(rl => roleNames.Contains(rl.RoleName)).ToList(); + var users = context.GetUsers().Where(usr => usernames.Contains(usr.Username)).ToList(); + var roles = context.GetRoles().Where(rl => roleNames.Contains(rl.RoleName)).ToList(); foreach (var user in users) { foreach (var role in roles) @@ -186,13 +189,14 @@ namespace MileageTraker.Web.Membership user.Roles.Add(role); } } + context.UpdateUser(user); } - context.SaveChanges(); } } public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) { + throw new NotSupportedException(); using (var context = new MileageTrakerContext()) { foreach (var username in usernames) diff --git a/Web/DAL/DataService.cs b/Web/DAL/DataService.cs index 4c03bf7..1ed3cdc 100644 --- a/Web/DAL/DataService.cs +++ b/Web/DAL/DataService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.SqlTypes; using System.Linq; using System.Linq.Expressions; using System.Text.RegularExpressions; @@ -440,6 +441,46 @@ namespace MileageTraker.Web.DAL #endregion + public void AddUser(User user) + { + user.Created = DateTime.Now; + + _db.Users.Add(user); + + _db.SaveChanges(); + } + + public void UpdateUser(User user) + { + _db.Entry(user).State = EntityState.Modified; + _db.SaveChanges(); + } + + /// + /// Update only fields not needing extra membership provider support + /// + public void UpdateUserPersonalInfo(User user) + { + var original = GetUser(user.UserId); + + original.Comment = user.Comment; + original.Email = user.Email; + original.FullName = user.FullName; + original.Username = user.Username; + + UpdateUser(original); + } + + public void UpdateUserLastActivity(string username) + { + var user = FindUserByUsername(username); + if (user != null) + { + user.LastActivityDate = DateTime.Now; + UpdateUser(user); + } + } + public IQueryable GetUsers() { return _db.Users; @@ -450,6 +491,27 @@ namespace MileageTraker.Web.DAL return _db.Users.Find(guid); } + public User FindUserByUsername(string username) + { + return _db.Users.FirstOrDefault(u => username.Equals(u.Username, StringComparison.InvariantCultureIgnoreCase)); + } + public User FindUserByEmail(string email) + { + return _db.Users.FirstOrDefault(u => email.Equals(u.Email, StringComparison.InvariantCultureIgnoreCase)); + } + + public IQueryable GetRoles() + { + return _db.Roles; + } + + public Role GetRoleByName(string roleName) + { + return + (from role in _db.Roles + where roleName == role.RoleName + select role).FirstOrDefault(); + } } } \ No newline at end of file diff --git a/Web/Membership/WebSecurity.cs b/Web/Membership/WebSecurity.cs deleted file mode 100644 index 1507e20..0000000 --- a/Web/Membership/WebSecurity.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Principal; -using System.Web; -using System.Web.Security; -using MileageTraker.Web.Context; - -namespace MileageTraker.Web.Membership -{ - public static class WebSecurity - { - public static HttpContextBase Context - { - get { return new HttpContextWrapper(HttpContext.Current); } - } - - public static HttpRequestBase Request - { - get { return Context.Request; } - } - - public static HttpResponseBase Response - { - get { return Context.Response; } - } - - public static IPrincipal User - { - get { return Context.User; } - } - - public static bool IsAuthenticated - { - get { return User.Identity.IsAuthenticated; } - } - - public static MembershipCreateStatus Register(string username, string password, string email, bool isApproved, - string firstName, string lastName) - { - MembershipCreateStatus createStatus; - System.Web.Security.Membership.CreateUser(username, password, email, null, null, isApproved, Guid.NewGuid(), - out createStatus); - - if (createStatus == MembershipCreateStatus.Success) - { - using (var context = new MileageTrakerContext()) - { - var user = context.Users.First(usr => usr.Username == username); - user.FirstName = firstName; - user.LastName = lastName; - context.SaveChanges(); - } - - if (isApproved) - { - FormsAuthentication.SetAuthCookie(username, false); - } - } - - return createStatus; - } - - public static Boolean Login(string username, string password, bool persistCookie = false) - { - var success = System.Web.Security.Membership.ValidateUser(username, password); - if (success) - { - FormsAuthentication.SetAuthCookie(username, persistCookie); - } - return success; - } - - public static void Logout() - { - FormsAuthentication.SignOut(); - } - - public static MembershipUser GetUser(string username) - { - return System.Web.Security.Membership.GetUser(username); - } - - public static bool ChangePassword(string userName, string currentPassword, string newPassword) - { - var success = false; - try - { - var currentUser = System.Web.Security.Membership.GetUser(userName, true); - success = currentUser.ChangePassword(currentPassword, newPassword); - } - catch (ArgumentException) - { - } - - return success; - } - - public static bool DeleteUser(string username) - { - return System.Web.Security.Membership.DeleteUser(username); - } - - public static Guid GetUserId(string userName) - { - var user = System.Web.Security.Membership.GetUser(userName); - return (Guid) user.ProviderUserKey; - } - - public static string CreateAccount(string userName, string password) - { - return CreateAccount(userName, password, requireConfirmationToken: false); - } - - public static string CreateAccount(string userName, string password, bool requireConfirmationToken = false) - { - var codeFirstMembership = (CodeFirstMembershipProvider)System.Web.Security.Membership.Provider; - return codeFirstMembership.CreateAccount(userName, password, requireConfirmationToken); - } - - public static string CreateUserAndAccount(string userName, string password) - { - return CreateUserAndAccount(userName, password, propertyValues: null); - } - - public static string CreateUserAndAccount(string userName, string password, bool requireConfirmation) - { - return CreateUserAndAccount(userName, password, null, requireConfirmation); - } - - public static string CreateUserAndAccount(string userName, string password, IDictionary values) - { - return CreateUserAndAccount(userName, password, values, requireConfirmationToken: false); - } - - private static string CreateUserAndAccount(string userName, string password, object propertyValues = null, - bool requireConfirmationToken = false) - { - var codeFirstMembership = (CodeFirstMembershipProvider)System.Web.Security.Membership.Provider; - - //IDictionary values = null; - //if (propertyValues != null) - //{ - // values = new RouteValueDictionary(propertyValues); - //} - - return codeFirstMembership.CreateUserAndAccount(userName, password, requireConfirmationToken); - } - - public static List FindUsersByEmail(string email, int pageIndex, int pageSize) - { - int totalRecords; - return - System.Web.Security.Membership.FindUsersByEmail(email, pageIndex, pageSize, out totalRecords) - .Cast() - .ToList(); - } - - public static List FindUsersByName(string username, int pageIndex, int pageSize) - { - int totalRecords; - return - System.Web.Security.Membership.FindUsersByName(username, pageIndex, pageSize, out totalRecords) - .Cast() - .ToList(); - } - - public static List GetAllUsers(int pageIndex, int pageSize) - { - int totalRecords; - return - System.Web.Security.Membership.GetAllUsers(pageIndex, pageSize, out totalRecords).Cast().ToList(); - } - } -} \ No newline at end of file diff --git a/Web/Migrations/201212191949311_AddMembership.Designer.cs b/Web/Migrations/201212191949311_AddMembership.Designer.cs deleted file mode 100644 index 3ea79cf..0000000 --- a/Web/Migrations/201212191949311_AddMembership.Designer.cs +++ /dev/null @@ -1,24 +0,0 @@ -// -namespace MileageTraker.Web.Migrations -{ - using System.Data.Entity.Migrations; - using System.Data.Entity.Migrations.Infrastructure; - - public sealed partial class AddMembership : IMigrationMetadata - { - string IMigrationMetadata.Id - { - get { return "201212191949311_AddMembership"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return "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"; } - } - } -} diff --git a/Web/Migrations/201212261822498_AddMembership.Designer.cs b/Web/Migrations/201212261822498_AddMembership.Designer.cs new file mode 100644 index 0000000..8d46341 --- /dev/null +++ b/Web/Migrations/201212261822498_AddMembership.Designer.cs @@ -0,0 +1,24 @@ +// +namespace MileageTraker.Web.Migrations +{ + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + + public sealed partial class AddMembership : IMigrationMetadata + { + string IMigrationMetadata.Id + { + get { return "201212261822498_AddMembership"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return "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"; } + } + } +} diff --git a/Web/Migrations/201212191949311_AddMembership.cs b/Web/Migrations/201212261822498_AddMembership.cs similarity index 71% rename from Web/Migrations/201212191949311_AddMembership.cs rename to Web/Migrations/201212261822498_AddMembership.cs index a04fced..314b3e3 100644 --- a/Web/Migrations/201212191949311_AddMembership.cs +++ b/Web/Migrations/201212261822498_AddMembership.cs @@ -11,26 +11,25 @@ namespace MileageTraker.Web.Migrations c => new { UserId = c.Guid(nullable: false), - Username = c.String(nullable: false), - Email = c.String(), - Password = c.String(nullable: false), - FirstName = c.String(), - LastName = c.String(), + Username = c.String(nullable: false, maxLength: 64), + Email = c.String(nullable: false, maxLength: 64), + FullName = c.String(nullable: false, maxLength: 128), + Password = c.String(nullable: false, maxLength: 128), Comment = c.String(), IsApproved = c.Boolean(nullable: false), PasswordFailuresSinceLastSuccess = c.Int(nullable: false), - LastPasswordFailureDate = c.DateTime(), - LastActivityDate = c.DateTime(), - LastLockoutDate = c.DateTime(), - LastLoginDate = c.DateTime(), - ConfirmationToken = c.String(), - CreateDate = c.DateTime(), + LastPasswordFailureDate = c.DateTime(nullable: false), + LastActivityDate = c.DateTime(nullable: false), + LastLockoutDate = c.DateTime(nullable: false), + LastLoginDate = c.DateTime(nullable: false), + Created = c.DateTime(nullable: false), IsLockedOut = c.Boolean(nullable: false), - LastPasswordChangedDate = c.DateTime(), - PasswordVerificationToken = c.String(), - PasswordVerificationTokenExpirationDate = c.DateTime(), + LastPasswordChangedDate = c.DateTime(nullable: false), + PasswordResetToken = c.String(maxLength: 128), }) - .PrimaryKey(t => t.UserId); + .PrimaryKey(t => t.UserId) + .Index(t => t.Username, unique: true) + .Index(t => t.Email, unique: true); CreateTable( "Role", @@ -71,9 +70,8 @@ namespace MileageTraker.Web.Migrations ([UserId] ,[Username] ,[Email] + ,[FullName] ,[Password] - ,[FirstName] - ,[LastName] ,[Comment] ,[IsApproved] ,[PasswordFailuresSinceLastSuccess] @@ -81,34 +79,29 @@ namespace MileageTraker.Web.Migrations ,[LastActivityDate] ,[LastLockoutDate] ,[LastLoginDate] - ,[ConfirmationToken] - ,[CreateDate] + ,[Created] ,[IsLockedOut] ,[LastPasswordChangedDate] - ,[PasswordVerificationToken] - ,[PasswordVerificationTokenExpirationDate]) + ,[PasswordResetToken]) VALUES ('33EA9A48-628A-458C-A9C9-FBB318C3CF0E' + ,'james.kolpack' ,'james.kolpack@gmail.com' - ,'james.kolpack@gmail.com' + ,'James Kolpack' ,'AH6VqEzZky4QfexgSwJIgg5gHrjaKGEk7pxUVBZz7X1Vkl31TlCUz9yTxmJN3/IDag==' - ,'James' - ,'Kolpack' ,NULL ,1 ,0 ,'1753-1-1' - ,GETDATE() ,'1753-1-1' - ,NULL - ,NULL + ,'1753-1-1' + ,'1753-1-1' ,GETDATE() ,0 - ,GETDATE() - ,NULL + ,'1753-1-1' ,NULL)"); - Sql(@"INSERT INTO [RoleUser] + Sql(@"INSERT INTO [RoleUser] ([Role_RoleId] ,[User_UserId]) VALUES diff --git a/Web/Models/User.cs b/Web/Models/User.cs index 4981598..268ef13 100644 --- a/Web/Models/User.cs +++ b/Web/Models/User.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Data.SqlTypes; using System.Web.Mvc; using System.Web.Security; @@ -10,66 +11,85 @@ namespace MileageTraker.Web.Models { [Key] [HiddenInput(DisplayValue = false)] - public virtual Guid UserId { get; set; } + public Guid UserId { get; set; } [Required] - [Display(Name = "Email")] - [DataType(DataType.EmailAddress)] - public virtual String Username { get; set; } + [StringLength(64)] + public string Username { get; set; } - [HiddenInput(DisplayValue = false)] + [Required] + [StringLength(64)] [DataType(DataType.EmailAddress)] - public virtual String Email { get; set; } + public string Email { get; set; } + + [Required] + [StringLength(128)] + [RegularExpression(@"[A-Za-z().]+(\s+[A-Za-z().]+)+", ErrorMessage = "Need complete name")] + public string FullName { get; set; } [Required, DataType(DataType.Password)] - public virtual String Password { get; set; } - - public virtual String FirstName { get; set; } - public virtual String LastName { get; set; } + [StringLength(128)] + public string Password { get; set; } [DataType(DataType.MultilineText)] - public virtual String Comment { get; set; } + public string Comment { get; set; } [HiddenInput(DisplayValue = false)] - public virtual Boolean IsApproved { get; set; } + public bool IsApproved { get; set; } [HiddenInput] - public virtual int PasswordFailuresSinceLastSuccess { get; set; } - - public virtual DateTime? LastPasswordFailureDate { get; set; } - public virtual DateTime? LastActivityDate { get; set; } - public virtual DateTime? LastLockoutDate { get; set; } - public virtual DateTime? LastLoginDate { get; set; } + public int PasswordFailuresSinceLastSuccess { get; set; } [HiddenInput(DisplayValue = false)] - public virtual String ConfirmationToken { get; set; } - - [HiddenInput] - public virtual DateTime? CreateDate { get; set; } - - [HiddenInput] - public virtual Boolean IsLockedOut { get; set; } - - [HiddenInput] - public virtual DateTime? LastPasswordChangedDate { get; set; } - - // TODO: change to Password Reset Token - [HiddenInput(DisplayValue = false)] - public virtual String PasswordVerificationToken { get; set; } + [DataType(DataType.Date)] + public DateTime LastPasswordFailureDate { get; set; } [HiddenInput(DisplayValue = false)] - public virtual DateTime? PasswordVerificationTokenExpirationDate { get; set; } + [DataType(DataType.DateTime)] + public DateTime LastActivityDate { get; set; } + + [HiddenInput(DisplayValue = false)] + [DataType(DataType.Date)] + public DateTime LastLockoutDate { get; set; } + + [HiddenInput(DisplayValue = false)] + [DataType(DataType.Date)] + public DateTime LastLoginDate { get; set; } + + [HiddenInput(DisplayValue = false)] + [DataType(DataType.Date)] + public DateTime Created { get; set; } + + [HiddenInput] + public bool IsLockedOut { get; set; } + + [HiddenInput] + [DataType(DataType.Date)] + public DateTime LastPasswordChangedDate { get; set; } + + [HiddenInput(DisplayValue = false)] + [StringLength(128)] + public string PasswordResetToken { get; set; } [HiddenInput] public virtual ICollection Roles { get; set; } - public MembershipUser CloneToMembershipUser(string providerName) + public User() + { + LastActivityDate = SqlDateTime.MinValue.Value; + LastLockoutDate = SqlDateTime.MinValue.Value; + LastLoginDate = SqlDateTime.MinValue.Value; + LastPasswordChangedDate = SqlDateTime.MinValue.Value; + LastPasswordFailureDate = SqlDateTime.MinValue.Value; + } + + public MembershipUser ToMembershipUser(string providerName) { return new MembershipUser(providerName, Username, UserId, Email, null, null, - IsApproved, IsLockedOut, CreateDate.Value, - LastLoginDate.Value, LastActivityDate.Value, - LastPasswordChangedDate.Value, LastLockoutDate.Value); + IsApproved, IsLockedOut, Created, + LastLoginDate, LastActivityDate, + LastPasswordChangedDate, LastLockoutDate); } } } \ No newline at end of file diff --git a/Web/Scripts/Shared/Site.js b/Web/Scripts/Shared/Site.js index 1b2ad57..d4ac259 100644 --- a/Web/Scripts/Shared/Site.js +++ b/Web/Scripts/Shared/Site.js @@ -86,12 +86,12 @@ $(function () { text: "

Recent Logs...

", }, style: { - width: 320, + width: 310, classes: 'qtip-light' }, position: { my: "top right", - at: "bottom left" + at: "bottom right" }, hide: { fixed: true, @@ -114,6 +114,19 @@ $(function () { } }); +$(function() { + // move checkbox elements into their labels + $('label[for]').each(function() { + var $label = $(this); + var $for = $label.attr('for'); + var $input = $('input#' + $for + ':checkbox'); + if ($input.length > 0) { + $input.prependTo($label); + $label.addClass('checkbox'); + } + }); +}); + $(function() { // Add active class to nav var idNavActiveRegex = { 'log-nav': /\/log/i, 'vehicle-nav': /\/vehicle/i }; @@ -160,6 +173,12 @@ $(function() { } }); +$(function () { + $('input#Username').keyup(function (e) { + $('input#Email').val($(this).val() + "@ethra.org"); + }); +}); + $(function() { // add qtip $("a.qtip-modal").each(function () { bindQtipModal($(this)); }); diff --git a/Web/Utility/Algorithms.cs b/Web/Utility/Algorithms.cs index 20cbccd..997a7cb 100644 --- a/Web/Utility/Algorithms.cs +++ b/Web/Utility/Algorithms.cs @@ -1,6 +1,8 @@ using System; using System.Linq; using System.Collections.Generic; +using System.Security.Cryptography; +using System.Web; namespace MileageTraker.Web.Utility { @@ -83,5 +85,22 @@ namespace MileageTraker.Web.Utility || existingDate < date && existingNumber < number || existingDate > date && existingNumber > number; } + + private const int TokenSizeInBytes = 16; + + public static string GenerateToken() + { + using (var prng = new RNGCryptoServiceProvider()) + { + return GenerateToken(prng); + } + } + + private static string GenerateToken(RandomNumberGenerator generator) + { + var tokenBytes = new byte[TokenSizeInBytes]; + generator.GetBytes(tokenBytes); + return HttpServerUtility.UrlTokenEncode(tokenBytes); + } } } \ No newline at end of file diff --git a/Web/Membership/Crypto.cs b/Web/Utility/Crypto.cs similarity index 69% rename from Web/Membership/Crypto.cs rename to Web/Utility/Crypto.cs index 1fc6161..61ed17b 100644 --- a/Web/Membership/Crypto.cs +++ b/Web/Utility/Crypto.cs @@ -1,10 +1,8 @@ -using System; -using System.Globalization; +using System; using System.Runtime.CompilerServices; using System.Security.Cryptography; -using System.Text; -namespace MileageTraker.Web.Membership +namespace MileageTraker.Web.Utility { public static class Crypto { @@ -12,34 +10,6 @@ namespace MileageTraker.Web.Membership private const int Pbkdf2SubkeyLength = 256/8; private const int SaltSize = 128/8; - private static string Hash(string input, string algorithm = "sha256") - { - if (input == null) - { - throw new ArgumentNullException("input"); - } - - return Hash(Encoding.UTF8.GetBytes(input), algorithm); - } - - private static string Hash(byte[] input, string algorithm = "sha256") - { - if (input == null) - { - throw new ArgumentNullException("input"); - } - - using (var alg = HashAlgorithm.Create(algorithm)) - { - if (alg != null) - { - var hashData = alg.ComputeHash(input); - return BinaryToHex(hashData); - } - throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "not supported hash alg", algorithm)); - } - } - /* ======================= * HASHED PASSWORD FORMATS * ======================= @@ -99,21 +69,7 @@ namespace MileageTraker.Web.Membership } return ByteArraysEqual(storedSubkey, generatedSubkey); } - - private static string BinaryToHex(byte[] data) - { - var hex = new char[data.Length*2]; - - for (int iter = 0; iter < data.Length; iter++) - { - var hexChar = ((byte) (data[iter] >> 4)); - hex[iter*2] = (char) (hexChar > 9 ? hexChar + 0x37 : hexChar + 0x30); - hexChar = ((byte) (data[iter] & 0xF)); - hex[iter*2 + 1] = (char) (hexChar > 9 ? hexChar + 0x37 : hexChar + 0x30); - } - return new string(hex); - } - + // Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized. [MethodImpl(MethodImplOptions.NoOptimization)] private static bool ByteArraysEqual(byte[] a, byte[] b) diff --git a/Web/Utility/CustomExtensions.cs b/Web/Utility/CustomExtensions.cs index 6977708..92a0e8a 100644 --- a/Web/Utility/CustomExtensions.cs +++ b/Web/Utility/CustomExtensions.cs @@ -171,7 +171,7 @@ namespace MileageTraker.Web.Utility /// public static DateTime UserOnlineThreshold() { - return DateTime.UtcNow.Subtract( + return DateTime.Now.Subtract( TimeSpan.FromMinutes( Convert.ToDouble( System.Web.Security.Membership.UserIsOnlineTimeWindow))); diff --git a/Web/ViewModels/Account/LoginViewModel.cs b/Web/ViewModels/Account/LoginViewModel.cs index 1588baf..ecf53e3 100644 --- a/Web/ViewModels/Account/LoginViewModel.cs +++ b/Web/ViewModels/Account/LoginViewModel.cs @@ -1,18 +1,15 @@ using System.ComponentModel.DataAnnotations; using MileageTraker.Web.Attributes; -using MileageTraker.Web.Utility; namespace MileageTraker.Web.ViewModels.Account { public class LoginViewModel { [Required] - [Display(Name = "Email Address")] - public string EmailAddress { get; set; } + public string Username { get; set; } [Required] [DataType(DataType.Password)] - [Display(Name = "Password")] public string Password { get; set; } [Display(Name = "Remember me?")] diff --git a/Web/ViewModels/User/CreateUserViewModel.cs b/Web/ViewModels/User/CreateUserViewModel.cs index 8266ddd..b58e002 100644 --- a/Web/ViewModels/User/CreateUserViewModel.cs +++ b/Web/ViewModels/User/CreateUserViewModel.cs @@ -1,32 +1,55 @@ -using System.ComponentModel.DataAnnotations; -using System.Web.Mvc; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using AutoMapper; +using MileageTraker.Web.Attributes; +using MileageTraker.Web.DAL; +using MileageTraker.Web.Models; namespace MileageTraker.Web.ViewModels.User { public class CreateUserViewModel { + [Required] + [StringLength(64)] + [InputSize("small")] + public string Username { get; set; } + [Required] [DataType(DataType.EmailAddress)] [RegularExpression(@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}", ErrorMessage = "Must be an email address")] + [InputSize("large")] public string Email { get; set; } [Required] - [StringLength(50, MinimumLength = 2)] - public string FirstName { get; set; } + [StringLength(128)] + [RegularExpression(@"[A-Za-z().]+(\s+[A-Za-z().]+)+", ErrorMessage = "Need complete name")] + [InputSize("medium")] + public string FullName { get; set; } - [Required] - [StringLength(50, MinimumLength = 2)] - public string LastName { get; set; } + [Required, DataType(DataType.Password)] + [StringLength(16, MinimumLength = 6)] + public string Password { get; set; } - public MultiSelectList Roles { get; set; } + public string[] Roles { get; set; } + + public IList AvailableRoles { get; set; } + public IList SelectedRoles { get; set; } static CreateUserViewModel() { Mapper.CreateMap(); + Mapper.CreateMap() + .ConvertUsing( + roleName => + { + using (var ds = new DataService()) + { + return ds.GetRoleByName(roleName); + } + }); } - public Models.User CloneToUser() + public Models.User ToUser() { var user = new Models.User(); Mapper.Map(this, user); diff --git a/Web/Views/Shared/EditorTemplates/_FieldLayout.cshtml b/Web/Views/Shared/EditorTemplates/_FieldLayout.cshtml index 37c6b22..d43d091 100644 --- a/Web/Views/Shared/EditorTemplates/_FieldLayout.cshtml +++ b/Web/Views/Shared/EditorTemplates/_FieldLayout.cshtml @@ -2,9 +2,10 @@ @{ Layout = null; var lowerPropertyName = @CustomExtensions.LowercaseFirst(ViewData.ModelMetadata.PropertyName); - var units = (string)ViewData.ModelMetadata.AdditionalValues["Units"]; - var formatHint = (string)ViewData.ModelMetadata.AdditionalValues["FormatHint"]; - var editLabel = (bool)ViewData.ModelMetadata.AdditionalValues["EditLabel"]; + var values = ViewData.ModelMetadata.AdditionalValues; + var units = values.ContainsKey("Units") ? (string)values["Units"] : null; + var formatHint = values.ContainsKey("FormatHint") ? (string)values["FormatHint"] : null; + var editLabel = values.ContainsKey("EditLabel") ? (bool) values["EditLabel"] : true; }
diff --git a/Web/Views/Shared/_Layout.cshtml b/Web/Views/Shared/_Layout.cshtml index cc60f34..85b1d67 100644 --- a/Web/Views/Shared/_Layout.cshtml +++ b/Web/Views/Shared/_Layout.cshtml @@ -40,7 +40,7 @@
-
+
@RenderBody()
diff --git a/Web/Views/Shared/_Roles.cshtml b/Web/Views/Shared/_Roles.cshtml new file mode 100644 index 0000000..44edcc5 --- /dev/null +++ b/Web/Views/Shared/_Roles.cshtml @@ -0,0 +1,10 @@ +@model MileageTraker.Web.ViewModels.User.CreateUserViewModel +@{ + Layout = "~/Views/Shared/EditorTemplates/_FieldLayout.cshtml"; +} +@Html.CheckBoxListFor( + m => m.Roles, + m => m.AvailableRoles, + m => m, + m => m, + m => m.Roles) \ No newline at end of file diff --git a/Web/Views/Shared/_ValidationSummary.cshtml b/Web/Views/Shared/_ValidationSummary.cshtml index 2fc18e7..9f8761d 100644 --- a/Web/Views/Shared/_ValidationSummary.cshtml +++ b/Web/Views/Shared/_ValidationSummary.cshtml @@ -4,4 +4,4 @@ × @Html.ValidationSummary(true)
-} \ No newline at end of file +} \ No newline at end of file diff --git a/Web/Views/User/Create.cshtml b/Web/Views/User/Create.cshtml index 526d7dd..4b6fbaf 100644 --- a/Web/Views/User/Create.cshtml +++ b/Web/Views/User/Create.cshtml @@ -13,6 +13,7 @@
@Html.EditorForModel() + @Html.Partial("_Roles", Model)
diff --git a/Web/Views/User/Details.cshtml b/Web/Views/User/Details.cshtml index 36771c1..9e3f218 100644 --- a/Web/Views/User/Details.cshtml +++ b/Web/Views/User/Details.cshtml @@ -8,9 +8,8 @@

@ViewBag.Title

+@Html.DisplayFor(m => m.Email) @Html.DisplayFor(m => m.Username) -@Html.DisplayFor(m => m.FirstName) -@Html.DisplayFor(m => m.LastName)
diff --git a/Web/Views/User/Index.cshtml b/Web/Views/User/Index.cshtml index 1e06f02..9dc4158 100644 --- a/Web/Views/User/Index.cshtml +++ b/Web/Views/User/Index.cshtml @@ -18,17 +18,29 @@ @grid.GetHtml(columns: grid.Columns( grid.Column("Username", format: - @@Html.Encode(item.Username) + @ + + @Html.Encode(item.Username) + @if (item.LastActivityDate > CustomExtensions.UserOnlineThreshold()) { Online } @if (item.IsLockedOut) { Locked Out } ), - grid.Column("FirstName", "First Name"), - grid.Column("LastName", "Last Name"), - grid.Column("Roles", format: item => - string.Join(", ", Roles.Provider.GetRolesForUser(item.Username) )), + grid.Column("FullName", "Full Name"), + grid.Column("Roles", format: + @ + @{ var roles = Roles.Provider.GetRolesForUser(item.Username); } + @if (roles.Length > 0) + { + @Html.Encode(string.Join(", ", roles)) + } + else + { + No Role + } + ), grid.Column(format: @
@Html.ActionLink("Edit", "Edit", new { id = item.UserId }, new { @class = "btn btn-mini" }) diff --git a/Web/Web.config b/Web/Web.config index f809e2a..a5e31c2 100644 --- a/Web/Web.config +++ b/Web/Web.config @@ -40,12 +40,12 @@ - + - + @@ -57,12 +57,16 @@ - + + + + + diff --git a/Web/Web.csproj b/Web/Web.csproj index 2606002..4bd80d4 100644 --- a/Web/Web.csproj +++ b/Web/Web.csproj @@ -64,6 +64,9 @@ True ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + ..\packages\MvcCheckBoxList.1.4.3.0\lib\MvcCheckBoxList.dll + @@ -112,16 +115,15 @@ + - - - - + + 201204181847082_InitialMigration.cs @@ -130,13 +132,14 @@ 201212042021486_AddVehiclePreviousLog.cs - - - 201212191949311_AddMembership.cs + + + 201212261822498_AddMembership.cs + @@ -270,6 +273,7 @@ + diff --git a/Web/packages.config b/Web/packages.config index 31836e9..4e0ad00 100644 --- a/Web/packages.config +++ b/Web/packages.config @@ -11,5 +11,6 @@ + \ No newline at end of file