Authentication added. Login/Logout operational.

This commit is contained in:
2012-12-20 21:39:13 -05:00
parent b30d3068e7
commit f129142dab
24 changed files with 640 additions and 65 deletions
+173
View File
@@ -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.";
}
}
}
}
+1
View File
@@ -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();
+1
View File
@@ -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();
+4 -6
View File
@@ -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();
+7 -39
View File
@@ -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];
+10 -19
View File
@@ -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)
{
}
}
}
+24
View File
@@ -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");
}
}
}
+57
View File
@@ -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; }
}
}
+28
View File
@@ -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>
}
+14
View File
@@ -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")
+33
View File
@@ -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>
}
+1
View File
@@ -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)
}
+3
View File
@@ -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>
}
+25
View File
@@ -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
View File
@@ -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>
+16
View File
@@ -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>