Authentication added. Login/Logout operational.
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Security;
|
||||
using MileageTraker.Web.Membership;
|
||||
using MileageTraker.Web.ViewModels;
|
||||
|
||||
namespace MileageTraker.Web.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
public class AccountController : Controller
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public ActionResult Login(string returnUrl)
|
||||
{
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Login(LoginViewModel model, string returnUrl)
|
||||
{
|
||||
if (ModelState.IsValid && WebSecurity.Login(model.EmailAddress, model.Password, model.RememberMe))
|
||||
{
|
||||
// TODO: send notification to user
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
ModelState.AddModelError("", "The user name or password provided is incorrect.");
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult LogOff()
|
||||
{
|
||||
WebSecurity.Logout();
|
||||
|
||||
// TODO: send notification to user
|
||||
return RedirectToAction("Login");
|
||||
}
|
||||
|
||||
[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));
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Manage(ChangePasswordViewModel model)
|
||||
{
|
||||
ViewBag.ReturnUrl = Url.Action("Manage");
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
// ChangePassword will throw an exception rather than return false in certain failure scenarios.
|
||||
bool changePasswordSucceeded;
|
||||
try
|
||||
{
|
||||
changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
changePasswordSucceeded = false;
|
||||
}
|
||||
|
||||
if (changePasswordSucceeded)
|
||||
{
|
||||
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
|
||||
}
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
return View(model);
|
||||
}
|
||||
|
||||
private ActionResult RedirectToLocal(string returnUrl)
|
||||
{
|
||||
if (Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
return RedirectToAction("Index", "Log");
|
||||
}
|
||||
|
||||
public enum ManageMessageId
|
||||
{
|
||||
ChangePasswordSuccess,
|
||||
SetPasswordSuccess,
|
||||
RemoveLoginSuccess,
|
||||
}
|
||||
|
||||
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.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using MileageTraker.Web.ViewModels;
|
||||
|
||||
namespace MileageTraker.Web.Controllers
|
||||
{
|
||||
[Authorize(Roles = "Administrator, Developer")]
|
||||
public class LogController : ControllerBase
|
||||
{
|
||||
private readonly DataService _dataService = new DataService();
|
||||
|
||||
@@ -6,6 +6,7 @@ using MileageTraker.Web.ViewModels;
|
||||
|
||||
namespace MileageTraker.Web.Controllers
|
||||
{
|
||||
[Authorize(Roles = "Administrator, Developer")]
|
||||
public class VehicleController : ControllerBase
|
||||
{
|
||||
private readonly DataService _ds = new DataService();
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace MileageTraker.Web.Membership
|
||||
}
|
||||
using (var context = new MileageTrakerContext())
|
||||
{
|
||||
Role role = context.Roles.FirstOrDefault(rl => rl.RoleName == roleName);
|
||||
var role = context.Roles.FirstOrDefault(rl => rl.RoleName == roleName);
|
||||
return role != null;
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,7 @@ namespace MileageTraker.Web.Membership
|
||||
}
|
||||
using (var context = new MileageTrakerContext())
|
||||
{
|
||||
User user;
|
||||
user = context.Users.FirstOrDefault(usr => usr.Username == username);
|
||||
var user = context.Users.FirstOrDefault(usr => usr.Username == username);
|
||||
if (user == null)
|
||||
{
|
||||
return false;
|
||||
@@ -70,7 +69,7 @@ namespace MileageTraker.Web.Membership
|
||||
}
|
||||
using (var context = new MileageTrakerContext())
|
||||
{
|
||||
Role role = context.Roles.FirstOrDefault(rl => rl.RoleName == roleName);
|
||||
var role = context.Roles.FirstOrDefault(rl => rl.RoleName == roleName);
|
||||
if (role != null)
|
||||
{
|
||||
return role.Users.Select(usr => usr.Username).ToArray();
|
||||
@@ -87,8 +86,7 @@ namespace MileageTraker.Web.Membership
|
||||
}
|
||||
using (var context = new MileageTrakerContext())
|
||||
{
|
||||
User user;
|
||||
user = context.Users.FirstOrDefault(Usr => Usr.Username == username);
|
||||
User user = context.Users.FirstOrDefault(Usr => Usr.Username == username);
|
||||
if (user != null)
|
||||
{
|
||||
return user.Roles.Select(rl => rl.RoleName).ToArray();
|
||||
|
||||
@@ -8,24 +8,11 @@ namespace MileageTraker.Web.Membership
|
||||
{
|
||||
public static class Crypto
|
||||
{
|
||||
private const int TokenSizeInBytes = 16;
|
||||
private const int Pbkdf2Count = 1000;
|
||||
private const int Pbkdf2SubkeyLength = 256/8;
|
||||
private const int SaltSize = 128/8;
|
||||
|
||||
|
||||
public static string GenerateSalt(int byteLength = SaltSize)
|
||||
{
|
||||
var Buff = new byte[byteLength];
|
||||
using (var Prng = new RNGCryptoServiceProvider())
|
||||
{
|
||||
Prng.GetBytes(Buff);
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(Buff);
|
||||
}
|
||||
|
||||
public static string Hash(string input, string algorithm = "sha256")
|
||||
private static string Hash(string input, string algorithm = "sha256")
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
@@ -35,37 +22,24 @@ namespace MileageTraker.Web.Membership
|
||||
return Hash(Encoding.UTF8.GetBytes(input), algorithm);
|
||||
}
|
||||
|
||||
public static string Hash(byte[] input, string algorithm = "sha256")
|
||||
private static string Hash(byte[] input, string algorithm = "sha256")
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
throw new ArgumentNullException("input");
|
||||
}
|
||||
|
||||
using (HashAlgorithm alg = HashAlgorithm.Create(algorithm))
|
||||
using (var alg = HashAlgorithm.Create(algorithm))
|
||||
{
|
||||
if (alg != null)
|
||||
{
|
||||
byte[] hashData = alg.ComputeHash(input);
|
||||
var hashData = alg.ComputeHash(input);
|
||||
return BinaryToHex(hashData);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "not supported hash alg", algorithm));
|
||||
}
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "not supported hash alg", algorithm));
|
||||
}
|
||||
}
|
||||
|
||||
public static string SHA1(string input)
|
||||
{
|
||||
return Hash(input, "sha1");
|
||||
}
|
||||
|
||||
public static string SHA256(string input)
|
||||
{
|
||||
return Hash(input, "sha256");
|
||||
}
|
||||
|
||||
/* =======================
|
||||
* HASHED PASSWORD FORMATS
|
||||
* =======================
|
||||
@@ -79,9 +53,7 @@ namespace MileageTraker.Web.Membership
|
||||
public static string HashPassword(string password)
|
||||
{
|
||||
if (password == null)
|
||||
{
|
||||
throw new ArgumentNullException("password");
|
||||
}
|
||||
|
||||
byte[] salt;
|
||||
byte[] subkey;
|
||||
@@ -101,15 +73,11 @@ namespace MileageTraker.Web.Membership
|
||||
public static bool VerifyHashedPassword(string hashedPassword, string password)
|
||||
{
|
||||
if (hashedPassword == null)
|
||||
{
|
||||
throw new ArgumentNullException("hashedPassword");
|
||||
}
|
||||
if (password == null)
|
||||
{
|
||||
throw new ArgumentNullException("password");
|
||||
}
|
||||
|
||||
byte[] hashedPasswordBytes = Convert.FromBase64String(hashedPassword);
|
||||
var hashedPasswordBytes = Convert.FromBase64String(hashedPassword);
|
||||
|
||||
// Verify a version 0 (see comment above) password hash.
|
||||
|
||||
@@ -132,7 +100,7 @@ namespace MileageTraker.Web.Membership
|
||||
return ByteArraysEqual(storedSubkey, generatedSubkey);
|
||||
}
|
||||
|
||||
internal static string BinaryToHex(byte[] data)
|
||||
private static string BinaryToHex(byte[] data)
|
||||
{
|
||||
var hex = new char[data.Length*2];
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ using MileageTraker.Web.Context;
|
||||
|
||||
namespace MileageTraker.Web.Membership
|
||||
{
|
||||
public sealed class WebSecurity
|
||||
public static class WebSecurity
|
||||
{
|
||||
public static HttpContextBase Context
|
||||
{
|
||||
@@ -121,17 +121,17 @@ namespace MileageTraker.Web.Membership
|
||||
|
||||
public static string CreateUserAndAccount(string userName, string password)
|
||||
{
|
||||
return CreateUserAndAccount(userName, password, propertyValues: null, requireConfirmationToken: false);
|
||||
return CreateUserAndAccount(userName, password, propertyValues: null);
|
||||
}
|
||||
|
||||
public static string CreateUserAndAccount(string userName, string password, bool requireConfirmation)
|
||||
{
|
||||
return CreateUserAndAccount(userName, password, propertyValues: null, requireConfirmationToken: requireConfirmation);
|
||||
return CreateUserAndAccount(userName, password, null, requireConfirmation);
|
||||
}
|
||||
|
||||
public static string CreateUserAndAccount(string userName, string password, IDictionary<string, object> values)
|
||||
{
|
||||
return CreateUserAndAccount(userName, password, propertyValues: values, requireConfirmationToken: false);
|
||||
return CreateUserAndAccount(userName, password, values, requireConfirmationToken: false);
|
||||
}
|
||||
|
||||
public static string CreateUserAndAccount(string userName, string password, object propertyValues = null,
|
||||
@@ -148,39 +148,30 @@ namespace MileageTraker.Web.Membership
|
||||
return codeFirstMembership.CreateUserAndAccount(userName, password, requireConfirmationToken, values);
|
||||
}
|
||||
|
||||
public static List<MembershipUser> FindUsersByEmail(string Email, int PageIndex, int PageSize)
|
||||
public static List<MembershipUser> FindUsersByEmail(string email, int pageIndex, int pageSize)
|
||||
{
|
||||
int totalRecords;
|
||||
return
|
||||
System.Web.Security.Membership.FindUsersByEmail(Email, PageIndex, PageSize, out totalRecords)
|
||||
System.Web.Security.Membership.FindUsersByEmail(email, pageIndex, pageSize, out totalRecords)
|
||||
.Cast<MembershipUser>()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static List<MembershipUser> FindUsersByName(string Username, int PageIndex, int PageSize)
|
||||
public static List<MembershipUser> FindUsersByName(string username, int pageIndex, int pageSize)
|
||||
{
|
||||
int totalRecords;
|
||||
return
|
||||
System.Web.Security.Membership.FindUsersByName(Username, PageIndex, PageSize, out totalRecords)
|
||||
System.Web.Security.Membership.FindUsersByName(username, pageIndex, pageSize, out totalRecords)
|
||||
.Cast<MembershipUser>()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static List<MembershipUser> GetAllUsers(int PageIndex, int PageSize)
|
||||
public static List<MembershipUser> GetAllUsers(int pageIndex, int pageSize)
|
||||
{
|
||||
int totalRecords;
|
||||
return
|
||||
System.Web.Security.Membership.GetAllUsers(PageIndex, PageSize, out totalRecords).Cast<MembershipUser>().ToList();
|
||||
System.Web.Security.Membership.GetAllUsers(pageIndex, pageSize, out totalRecords).Cast<MembershipUser>().ToList();
|
||||
}
|
||||
|
||||
public static void InitializeDatabaseConnection(string connectionStringName, string userTableName, string userIdColumn,
|
||||
string userNameColumn, bool autoCreateTables)
|
||||
{
|
||||
}
|
||||
|
||||
public static void InitializeDatabaseConnection(string connectionString, string providerName, string userTableName,
|
||||
string userIdColumn, string userNameColumn, bool autoCreateTables)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// <auto-generated />
|
||||
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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"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
namespace MileageTraker.Web.Migrations
|
||||
{
|
||||
using System.Data.Entity.Migrations;
|
||||
|
||||
public partial class AddMembership : DbMigration
|
||||
{
|
||||
public override void Up()
|
||||
{
|
||||
CreateTable(
|
||||
"User",
|
||||
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(),
|
||||
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(),
|
||||
IsLockedOut = c.Boolean(nullable: false),
|
||||
LastPasswordChangedDate = c.DateTime(),
|
||||
PasswordVerificationToken = c.String(),
|
||||
PasswordVerificationTokenExpirationDate = c.DateTime(),
|
||||
})
|
||||
.PrimaryKey(t => t.UserId);
|
||||
|
||||
CreateTable(
|
||||
"Role",
|
||||
c => new
|
||||
{
|
||||
RoleId = c.Guid(nullable: false),
|
||||
RoleName = c.String(nullable: false),
|
||||
Description = c.String(),
|
||||
})
|
||||
.PrimaryKey(t => t.RoleId);
|
||||
|
||||
CreateTable(
|
||||
"RoleUser",
|
||||
c => new
|
||||
{
|
||||
Role_RoleId = c.Guid(nullable: false),
|
||||
User_UserId = c.Guid(nullable: false),
|
||||
})
|
||||
.PrimaryKey(t => new { t.Role_RoleId, t.User_UserId })
|
||||
.ForeignKey("Role", t => t.Role_RoleId, cascadeDelete: true)
|
||||
.ForeignKey("User", t => t.User_UserId, cascadeDelete: true)
|
||||
.Index(t => t.Role_RoleId)
|
||||
.Index(t => t.User_UserId);
|
||||
|
||||
Sql(@"INSERT INTO [Role]
|
||||
([RoleId]
|
||||
,[RoleName]
|
||||
,[Description])
|
||||
VALUES
|
||||
('f5bc8c1f-9e8b-4fd8-87d9-27404d8995d5'
|
||||
,'Administrator'
|
||||
,'Administrator'),
|
||||
('bb3bec25-2a4d-467a-b790-e24b53a54717'
|
||||
,'Developer'
|
||||
,'Developer')");
|
||||
|
||||
Sql(@"INSERT INTO [User]
|
||||
([UserId]
|
||||
,[Username]
|
||||
,[Email]
|
||||
,[Password]
|
||||
,[FirstName]
|
||||
,[LastName]
|
||||
,[Comment]
|
||||
,[IsApproved]
|
||||
,[PasswordFailuresSinceLastSuccess]
|
||||
,[LastPasswordFailureDate]
|
||||
,[LastActivityDate]
|
||||
,[LastLockoutDate]
|
||||
,[LastLoginDate]
|
||||
,[ConfirmationToken]
|
||||
,[CreateDate]
|
||||
,[IsLockedOut]
|
||||
,[LastPasswordChangedDate]
|
||||
,[PasswordVerificationToken]
|
||||
,[PasswordVerificationTokenExpirationDate])
|
||||
VALUES
|
||||
('33EA9A48-628A-458C-A9C9-FBB318C3CF0E'
|
||||
,'james.kolpack@gmail.com'
|
||||
,'james.kolpack@gmail.com'
|
||||
,'AH6VqEzZky4QfexgSwJIgg5gHrjaKGEk7pxUVBZz7X1Vkl31TlCUz9yTxmJN3/IDag=='
|
||||
,'James'
|
||||
,'Kolpack'
|
||||
,NULL
|
||||
,1
|
||||
,0
|
||||
,NULL
|
||||
,NULL
|
||||
,NULL
|
||||
,NULL
|
||||
,NULL
|
||||
,GETDATE()
|
||||
,0
|
||||
,NULL
|
||||
,NULL
|
||||
,NULL)");
|
||||
|
||||
Sql(@"INSERT INTO [RoleUser]
|
||||
([Role_RoleId]
|
||||
,[User_UserId])
|
||||
VALUES
|
||||
('bb3bec25-2a4d-467a-b790-e24b53a54717'
|
||||
,'33EA9A48-628A-458C-A9C9-FBB318C3CF0E')");
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
DropIndex("RoleUser", new[] { "User_UserId" });
|
||||
DropIndex("RoleUser", new[] { "Role_RoleId" });
|
||||
DropForeignKey("RoleUser", "User_UserId", "User");
|
||||
DropForeignKey("RoleUser", "Role_RoleId", "Role");
|
||||
DropTable("RoleUser");
|
||||
DropTable("Role");
|
||||
DropTable("User");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace MileageTraker.Web.ViewModels
|
||||
{
|
||||
public class ChangePasswordViewModel
|
||||
{
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Current password")]
|
||||
public string OldPassword { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "New password")]
|
||||
public string NewPassword { get; set; }
|
||||
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm new password")]
|
||||
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
}
|
||||
|
||||
public class LoginViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "Email Address")]
|
||||
public string EmailAddress { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Password")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[Display(Name = "Remember me?")]
|
||||
public bool RememberMe { get; set; }
|
||||
}
|
||||
|
||||
public class RegisterModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "User name")]
|
||||
public string UserName { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Password")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm password")]
|
||||
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
@model MileageTraker.Web.ViewModels.LoginViewModel
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Log in";
|
||||
Layout = "~/Views/Shared/_Layout.login.cshtml";
|
||||
}
|
||||
@section Scripts
|
||||
{
|
||||
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
|
||||
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title</h2>
|
||||
|
||||
@using (Html.BeginForm("Login", "Account", new { ViewBag.ReturnUrl }, FormMethod.Post)) {
|
||||
@Html.AntiForgeryToken()
|
||||
@Html.ValidationSummary(true)
|
||||
|
||||
<fieldset>
|
||||
<legend />
|
||||
@Html.EditorForModel()
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<input type="submit" value="Log in" class="btn btn-primary" />
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
@model MileageTraker.Web.ViewModels.ChangePasswordViewModel
|
||||
@{
|
||||
ViewBag.Title = "Manage Account";
|
||||
}
|
||||
|
||||
<hgroup class="title">
|
||||
<h1>@ViewBag.Title</h1>
|
||||
</hgroup>
|
||||
|
||||
<p class="label label-success">@ViewBag.StatusMessage</p>
|
||||
|
||||
<p>You're logged in as <strong>@User.Identity.Name</strong>.</p>
|
||||
|
||||
@Html.Partial("_ChangePasswordPartial")
|
||||
@@ -0,0 +1,33 @@
|
||||
@model MileageTraker.Web.ViewModels.RegisterModel
|
||||
@{
|
||||
ViewBag.Title = "Register";
|
||||
}
|
||||
|
||||
<hgroup class="title">
|
||||
<h1>@ViewBag.Title</h1>
|
||||
<h2>Create a new account.</h2>
|
||||
</hgroup>
|
||||
|
||||
@using (Html.BeginForm()) {
|
||||
@Html.AntiForgeryToken()
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<fieldset>
|
||||
<legend>Registration Form</legend>
|
||||
<ol>
|
||||
<li>
|
||||
@Html.LabelFor(m => m.UserName)
|
||||
@Html.TextBoxFor(m => m.UserName)
|
||||
</li>
|
||||
<li>
|
||||
@Html.LabelFor(m => m.Password)
|
||||
@Html.PasswordFor(m => m.Password)
|
||||
</li>
|
||||
<li>
|
||||
@Html.LabelFor(m => m.ConfirmPassword)
|
||||
@Html.PasswordFor(m => m.ConfirmPassword)
|
||||
</li>
|
||||
</ol>
|
||||
<input type="submit" value="Register" />
|
||||
</fieldset>
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
@model MileageTraker.Web.ViewModels.ChangePasswordViewModel
|
||||
|
||||
<h3>Change password</h3>
|
||||
|
||||
@using (Html.BeginForm("Manage", "Account")) {
|
||||
@Html.AntiForgeryToken()
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<fieldset>
|
||||
<legend>Change Password Form</legend>
|
||||
<ol>
|
||||
<li>
|
||||
@Html.LabelFor(m => m.OldPassword)
|
||||
@Html.PasswordFor(m => m.OldPassword)
|
||||
</li>
|
||||
<li>
|
||||
@Html.LabelFor(m => m.NewPassword)
|
||||
@Html.PasswordFor(m => m.NewPassword)
|
||||
</li>
|
||||
<li>
|
||||
@Html.LabelFor(m => m.ConfirmPassword)
|
||||
@Html.PasswordFor(m => m.ConfirmPassword)
|
||||
</li>
|
||||
</ol>
|
||||
<input type="submit" value="Change password" />
|
||||
</fieldset>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
@model MileageTraker.Web.ViewModels.ChangePasswordViewModel
|
||||
|
||||
<p>
|
||||
You do not have a password for this site.
|
||||
</p>
|
||||
|
||||
@using (Html.BeginForm("Manage", "Account")) {
|
||||
@Html.AntiForgeryToken()
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<fieldset>
|
||||
<legend>Set Password Form</legend>
|
||||
<ol>
|
||||
<li>
|
||||
@Html.LabelFor(m => m.NewPassword)
|
||||
@Html.PasswordFor(m => m.NewPassword)
|
||||
</li>
|
||||
<li>
|
||||
@Html.LabelFor(m => m.ConfirmPassword)
|
||||
@Html.PasswordFor(m => m.ConfirmPassword)
|
||||
</li>
|
||||
</ol>
|
||||
<input type="submit" value="Set password" />
|
||||
</fieldset>
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
{
|
||||
@Html.ValidationSummary(true)
|
||||
<fieldset>
|
||||
<legend />
|
||||
|
||||
@Html.EditorForModel()
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<label class="checkbox">
|
||||
@Html.CheckBox("", ViewData.TemplateInfo.FormattedModelValue)
|
||||
@Html.LabelForModel()
|
||||
</label>
|
||||
@@ -0,0 +1,12 @@
|
||||
@{
|
||||
Layout = "~/Views/Shared/EditorTemplates/_FieldLayout.cshtml";
|
||||
var inputSize = (string)ViewData.ModelMetadata.AdditionalValues["InputSize"];
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(inputSize))
|
||||
{
|
||||
@Html.Password("", ViewData.TemplateInfo.FormattedModelValue, new { @class = inputSize})
|
||||
}
|
||||
else
|
||||
{
|
||||
@Html.Password("", ViewData.TemplateInfo.FormattedModelValue)
|
||||
}
|
||||
@@ -17,6 +17,9 @@
|
||||
<li id="vehicle-nav">@Html.ActionLink("Vehicles", "Index", "Vehicle")</li>
|
||||
<li>@Html.ActionLink("Enter Mileage Log", "Index", "CreateLog")</li>
|
||||
</ul>
|
||||
<section id="account-management">
|
||||
@Html.Partial("_Layout.authentication.cshtml")
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
@if (Request.IsAuthenticated) {
|
||||
<ul class="nav pull-right">
|
||||
<li>
|
||||
@Html.ActionLink(User.Identity.Name, "Manage", "Account", null, new { @class = "username", title = "Manage" })
|
||||
</li>
|
||||
<li>
|
||||
@using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "form-logout" })) {
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn-link">
|
||||
Log Off
|
||||
</button>
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Mileage Traker - ETHRA - @ViewBag.Title</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link href="@Url.Content("~/Content/themes/custom-theme/jquery-ui-1.9.2.custom.min.css")" rel="stylesheet" type="text/css" />
|
||||
<link href="@Url.Content("~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" />
|
||||
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
|
||||
@RenderSection("Styles", false)
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container-fluid">
|
||||
@RenderBody()
|
||||
</div>
|
||||
|
||||
<script src="@Url.Content("~/Scripts/jquery-1.8.3.min.js")" type="text/javascript"></script>
|
||||
<script src="@Url.Content("~/Scripts/bootstrap.min.js")" type="text/javascript"></script>
|
||||
<script src="@Url.Content("~/Scripts/jquery.numeric.js")" type="text/javascript"></script>
|
||||
<script src="@Url.Content("~/Scripts/jquery-ui-1.9.2.custom.min.js")" type="text/javascript"></script>
|
||||
<script src="@Url.Content("~/Scripts/Shared/Site.js")" type="text/javascript"></script>
|
||||
@RenderSection("Scripts", false)
|
||||
</body>
|
||||
</html>
|
||||
+3
-1
@@ -35,6 +35,9 @@
|
||||
<add namespace="System.Web.WebPages" />
|
||||
</namespaces>
|
||||
</pages>
|
||||
<authentication mode="Forms">
|
||||
<forms loginUrl="~/Account/LogIn" timeout="2880" />
|
||||
</authentication>
|
||||
<membership defaultProvider="CodeFirstMembershipProvider">
|
||||
<providers>
|
||||
<add name="CodeFirstMembershipProvider" type="MileageTraker.Web.Membership.CodeFirstMembershipProvider" connectionStringName="MileageTrakerContext" />
|
||||
@@ -42,7 +45,6 @@
|
||||
</membership>
|
||||
<roleManager enabled="true" defaultProvider="CodeFirstRoleProvider">
|
||||
<providers>
|
||||
<clear />
|
||||
<add name="CodeFirstRoleProvider" type="MileageTraker.Web.Membership.CodeFirstRoleProvider" connectionStringName="MileageTrakerContext" />
|
||||
</providers>
|
||||
</roleManager>
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Context\MileageTrakerContext.cs" />
|
||||
<Compile Include="Controllers\AccountController.cs" />
|
||||
<Compile Include="Controllers\CityController.cs" />
|
||||
<Compile Include="Controllers\ControllerBase.cs" />
|
||||
<Compile Include="Controllers\EmployeeController.cs" />
|
||||
@@ -123,11 +124,16 @@
|
||||
<Compile Include="Migrations\201212042021486_AddVehiclePreviousLog.Designer.cs">
|
||||
<DependentUpon>201212042021486_AddVehiclePreviousLog.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Migrations\201212191949311_AddMembership.cs" />
|
||||
<Compile Include="Migrations\201212191949311_AddMembership.Designer.cs">
|
||||
<DependentUpon>201212191949311_AddMembership.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Migrations\Configuration.cs" />
|
||||
<Compile Include="Models\Role.cs" />
|
||||
<Compile Include="Models\User.cs" />
|
||||
<Compile Include="Utility\ActionLogAttribute.cs" />
|
||||
<Compile Include="Utility\HttpParamActionAttribute.cs" />
|
||||
<Compile Include="ViewModels\AccountModels.cs" />
|
||||
<Compile Include="ViewModels\EmployeeMileageItem.cs" />
|
||||
<Compile Include="ViewModels\EmployeeMileageViewModel.cs" />
|
||||
<Compile Include="ViewModels\LogIndexViewModel.cs" />
|
||||
@@ -238,6 +244,15 @@
|
||||
<Content Include="Views\Shared\EditorTemplates\Date.cshtml" />
|
||||
<Content Include="Views\Shared\DisplayTemplates\Date.cshtml" />
|
||||
<Content Include="Views\Shared\EditorTemplates\Int32.cshtml" />
|
||||
<Content Include="Views\Account\Login.cshtml" />
|
||||
<Content Include="Views\Account\Manage.cshtml" />
|
||||
<Content Include="Views\Account\Register.cshtml" />
|
||||
<Content Include="Views\Account\_ChangePasswordPartial.cshtml" />
|
||||
<Content Include="Views\Account\_SetPasswordPartial.cshtml" />
|
||||
<Content Include="Views\Shared\EditorTemplates\Password.cshtml" />
|
||||
<Content Include="Views\Shared\EditorTemplates\Boolean.cshtml" />
|
||||
<Content Include="Views\Shared\_Layout.login.cshtml" />
|
||||
<Content Include="Views\Shared\_Layout.authentication.cshtml.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="packages.config" />
|
||||
@@ -356,6 +371,7 @@
|
||||
<None Include="Properties\PublishProfiles\ETHRA.pubxml" />
|
||||
<None Include="Properties\PublishProfiles\Localhost.pubxml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
|
||||
Reference in New Issue
Block a user