in·dom·i·ta·ble
adj.
Incapable of being overcome, subdued, or vanquished; unconquerable.
25th
FEB
Alt.Net Lunch and Learn: Asp.Net MVC
Posted by indomitablehef | Filed under Asp.Net MVC, Alt.Net
I’m presenting at the Nashville Alt.Net user group Lunch and Learn tomorrow at 11:30. New venue this time, but still in Maryland Farms. Check the details here: http://groups.google.com/group/nashvillealtnet-discuss
Oh, yeah…the topic: Introduction to Asp.Net MVC. As usual, the presentation will be short and hopefully the discussion will be long.
9th
FEB
Forms Authentication in Asp.Net MVC, Part III
Posted by indomitablehef | Filed under Asp.Net MVC
This is part 3 of 4
Forms Authentication in Asp.Net MVC
Moving on from where we left off in part II, we now need to implement our MembershipProvider.
We have two final tasks left to accomplish
- Retrieve the User from the datastore
- Check to see if the username and password are valid
Following the Single Responsibility Principle, we’ll separate these, using the MembershipProvider for validating the username/password, and a UserRepository for retrieving the user from the datastore.
From last time, recall that our IMembershipProvider looks like this:
1 2 3 4 | public interface IMembershipProvider { bool ValidateUser(string userName, string password, out string validationMessage); } |
Now we’re going to need a user repository, or some other form of User data access object. I’m using NHibernate, but you could use Linq to Sql or your ORM of choice. Either way, the interface for our IUserRepository is quite simple. I leave the implementation entirely up to you.
1 2 3 4 | public interface IUserRepository { User GetByUserName(string userName); } |
So, we can implement our MembershipProvider like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public class MembershipProvider : IMembershipProvider { private IUserRepository userRepository; public MembershipProvider(IUserRepository userRepository) { this.userRepository = userRepository; } public MembershipProvider() : this(ServiceLocator.Current.GetInstance<IUserRepository>()) { } public bool ValidateUser(string userName, string password, out string validationMessage) { User user; try { user = userRepository.GetByUserName(userName); if (user == null) { validationMessage = "invalid username"; return false; } if (user.Password != password) { validationMessage = "invalid password"; return false; } validationMessage = ""; return true; } catch (Exception ex) { throw new Exception("Encountered an error while attempting to validate username and password",ex); } } } |
And we can test it like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | [TestFixture] public class MembershipProviderTests { #region Setup/Teardown [SetUp] public void SetUp() { mockedUserRepository = MockRepository.GenerateMock<IUserRepository>(); provider = new MembershipProvider(mockedUserRepository); } #endregion private IMembershipProvider provider; private IUserRepository mockedUserRepository; [Test] public void IsValidWhenUserNameAndPasswordMatchExistingUser() { //arrange var user = MockRepository.GenerateStub<User>(); user.UserName = "billy.pilgrim"; user.Password = "tralfamador"; mockedUserRepository.Expect(x => x.GetByUserName("billy.pilgrim")).Return(user); string validationMessage = ""; //act var isValid = provider.ValidateUser("billy.pilgrim", "tralfamador", out validationMessage); //assert Assert.IsTrue(isValid); } [Test] public void IsInValidWhenUserDoesNotExist() { //arrange mockedUserRepository.Expect(x => x.GetByUserName("billy.pilgrim")).Return(null); string validationMessage = ""; //act var isValid = provider.ValidateUser("billy.pilgrim", "tralfamador", out validationMessage); //assert Assert.IsFalse(isValid); Assert.AreEqual("invalid username",validationMessage); } [Test] public void IsInValidWhenPasswordDoesNotMatch() { //arrange var user = MockRepository.GenerateStub<User>(); user.UserName = "billy.pilgrim"; user.Password = "tralfamador"; mockedUserRepository.Expect(x => x.GetByUserName("billy.pilgrim")).Return(user); string validationMessage = ""; //act var isValid = provider.ValidateUser("billy.pilgrim", "wrongpassword", out validationMessage); //assert Assert.IsFalse(isValid); Assert.AreEqual("invalid password",validationMessage); } } |
And we’re basically done. There’s one more installment coming, though. We’ll take a look at setting up the IoC container used in these examples (Castle Windsor) and do a little refactoring.
6th
FEB
Forms Authentication in Asp.Net MVC, Part II
Posted by indomitablehef | Filed under Asp.Net MVC
This is part 2 of 4
Forms Authentication in Asp.Net MVC
Continuing on from where we left off in part I, we now need to implement the code that actually does the Authentication against whatever user store we have in place. The default MVC project uses the Membership provider for Asp.Net, and that works just fine. I need more control than that, though, so let’s take a look at the next steps.
Last time, I mentioned the MembershipController and the Login action. Once the Login view has been rendered, it submits to the Authenticate action, also on the MembershipController.
The Login View:
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> <%@ Import Namespace="MyProject.Web.Controllers" %> <asp:Content ID="content" ContentPlaceHolderID="MainContentPlaceHolder" runat="server"> <h1> Login</h1> <% if( ViewData["ErrorMessage"] != null ){ %> <p> <% =ViewData["ErrorMessage"] %></p> <% } %> <% using( Html.BeginForm("Authenticate", "Membership" )){ %> <fieldset> <legend>Login</legend> <div> <label for="userName"> User Name:</label> <% =Html.TextBox( "userName" ) %></div> <div> <label for="password"> Password:</label> <% =Html.Password( "password" ) %></div> <div> <label for="rememberMe"> Remember Me:</label> <input type="checkbox" id="rememberMe" name="rememberMe" checked="checked" value="checked" /></div> <div> <% =Html.SubmitButton() %></div> <% =Html.Hidden( "returnUrl", "/" ) %> </fieldset> <% } %> </asp:Content>
And the login action on the MembershipController
1 2 3 4 5 6 7 8 | public class MembershipController: Controller { public ActionResult Login() { return View("Login"); } } |
Now we need an Authenticate action, for when the Login form is submitted
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public ActionResult Authenticate(string userName, string password, string rememberMe, string returnUrl) { string validationMessage = ""; if (membershipProvider.ValidateUser(userName, password, out validationMessage)) { authenticationProvider.SetAuthCookie(userName, (rememberMe != null)); return new RedirectResult(returnUrl); } else { ViewData["ErrorMessage"] = validationMessage; return View("Login"); } } |
Notice some things here. We’re using the authenticationProvider we started with in part I, and a new membershipProvider. We’ve also added another method to our AuthenticationProvider, SetAuthCookie. This simply wraps the FormsAuthentication.SetAuthCookie method.
So, now our IAuthenticationProvider looks like this:
1 2 3 4 5 6 | public interface IAuthenticationProvider { bool IsAuthenticated(ControllerContext context); void RedirectToLogin(ControllerContext context); void SetAuthCookie(string userName, bool createPersistentCookie); } |
Our FormsAuthenticationProvider implementation looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class FormsAuthenticationProvider: IAuthenticationProvider { public virtual bool IsAuthenticated(ControllerContext context) { return context.HttpContext.User.Identity.IsAuthenticated; } public virtual void RedirectToLogin(ControllerContext context) { context.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + string.Format("?ReturnUrl={0}", context.HttpContext.Request.Url.AbsolutePath), true); } public virtual void SetAuthCookie(string userName, bool createPersistentCookie) { FormsAuthentication.SetAuthCookie(userName, createPersistentCookie); } } |
And we need to provide the Authentication and Membership provider to our MembershipController, so we add them in the constructors. The whole thing, with the constructors, Login Action and Authenticate action looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public class MembershipController: Controller { private readonly IAuthenticationProvider authenticationProvider; private readonly IMembershipProvider membershipProvider; public MembershipController(IAuthenticationProvider authenticationProvider, IMembershipProvider membershipProvider) { this.authenticationProvider = authenticationProvider; this.membershipProvider = membershipProvider; } public MembershipController():this(ServiceLocator.Current.GetInstance<IAuthenticationProvider>(), ServiceLocator.Current.GetInstance<IMembershipProvider>()) {} public ActionResult Login() { return View("Login"); } public ActionResult Authenticate(string userName, string password, string rememberMe, string returnUrl) { string validationMessage = ""; if (membershipProvider.ValidateUser(userName, password, out validationMessage)) { authenticationProvider.SetAuthCookie(userName, (rememberMe != null)); return new RedirectResult(returnUrl); } else { ViewData["ErrorMessage"] = validationMessage; return View("Login"); } } } |
Now we can write some good tests for our MembershipController, mocking the MembershipProvider and the AuthenticationProvider
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | [TestFixture] public class MembershipControllerTests { private MembershipController controller; private IMembershipProvider mockedMembershipProvider; private IAuthenticationProvider mockedAuthenticationProvider; [SetUp] public void SetUp() { mockedMembershipProvider = MockRepository.GenerateMock<IMembershipProvider>(); mockedAuthenticationProvider = MockRepository.GenerateMock<IAuthenticationProvider>(); controller = new MembershipController(mockedAuthenticationProvider, mockedMembershipProvider); } [Test] public void CanInitUserLogin() { ViewResult result = controller.Login().AssertViewRendered(); } [Test] public void CanAuthenticate() { //arrange string validationMessage = ""; mockedMembershipProvider.Expect(x => x.ValidateUser("", "", out validationMessage)).IgnoreArguments().Return(true); mockedAuthenticationProvider.Expect(x => x.SetAuthCookie("", false)).IgnoreArguments(); //act, assert RedirectResult redirectResult = controller.Authenticate("username", "password", "rememberme", "/").AssertHttpRedirect(); mockedMembershipProvider.VerifyAllExpectations(); mockedAuthenticationProvider.VerifyAllExpectations(); } [Test] public void CanRedirectToLoginWhenNotAuthenticated() { //arrange var controller = new MembershipController(mockedAuthenticationProvider, mockedMembershipProvider); string validationMessage = ""; //when validate user returns false mockedMembershipProvider.Expect(x => x.ValidateUser("", "", out validationMessage)).IgnoreArguments().Return(false); //act, assert ViewResult viewResult = controller.Authenticate("username", "password", "rememberme", "/").AssertViewRendered(); Assert.IsNotNull(controller.ViewData["ErrorMessage"]); mockedAuthenticationProvider.AssertWasNotCalled(x => x.SetAuthCookie("username", true)); mockedMembershipProvider.VerifyAllExpectations(); } } |
Notice the AssertViewRendered and AssertHttpRedirect extension methods. These are part of the MvcContrib TestHelper library.
Finally, we’ll take a quick look at our IMembershipProvider interface
1 2 3 4 | public interface IMembershipProvider { bool ValidateUser(string userName, string password, out string validationMessage); } |
…but I’ll need to leave the implementation of that for part III, coming soon, which can be found here.
6th
Forms Authentication in Asp.Net MVC
Posted by indomitablehef | Filed under Asp.Net MVC, TDD
This is part 1 of 4
Forms Authentication in Asp.Net MVC
I’ve been working on a testable solution for my Asp.Net MVC project. I have a solution that I think works, but I’d love to hear comments/criticisms if you’ve got any.
First, set up forms authentication in web.config:
<authentication mode="Forms"> <forms loginUrl="Membership/Login" defaultUrl="/" /> </authentication>
Now you’ll need an interface for IAuthenticationProvider
1 2 3 4 5 | public interface IAuthenticationProvider { bool IsAuthenticated(ControllerContext context); void RedirectToLogin(ControllerContext context); } |
And an implementation, FormsAuthenticationProvider
1 2 3 4 5 6 7 8 9 10 11 12 | public class FormsAuthenticationProvider: IAuthenticationProvider { public virtual bool IsAuthenticated(ControllerContext context) { return context.HttpContext.User.Identity.IsAuthenticated; } public virtual void RedirectToLogin(ControllerContext context) { context.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + string.Format("?ReturnUrl={0}", context.HttpContext.Request.Url.AbsolutePath), true); } } |
I’m doing authentication on Controllers using an ActionFilterAttribute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class RequiresAuthenticationAttribute : ActionFilterAttribute { private IAuthenticationProvider authenticationProvider; public RequiresAuthenticationAttribute(IAuthenticationProvider provider) { authenticationProvider = provider; } public RequiresAuthenticationAttribute() : this(ServiceLocator.Current.GetInstance<IAuthenticationProvider>()) {} public override void OnActionExecuting(ActionExecutingContext filterContext) { if (!authenticationProvider.IsAuthenticated(filterContext)) { authenticationProvider.RedirectToLogin(filterContext); } } } |
A couple of things to notice about this:
I have a test-specific constructor, that allows me to pass in the IAuthentication provider. I’m using Castle Windsor as an IoC container, and the LocatorProvider for ServiceLocator. In my parameter-less constructor, I simply get the configured IAuthenticationProvider from the IoC container.
To use my RequiresAuthentication attribute, I just decorate the controller with it, like this
1 2 3 4 5 6 7 8 | [HandleError] [RequiresAuthentication] public class HomeController : Controller { public ActionResult Index() { return View(); } } |
Now for my tests
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | [TestFixture] public class RequiresAuthenticationAttributeTests { [Test] public void CanRedirectToLoginWhenNotAuthenticated() { //arrange var mockedProvider = MockRepository.GenerateMock<IAuthenticationProvider>(); mockedProvider.Expect(x => x.IsAuthenticated(null)).IgnoreArguments().Return(false); mockedProvider.Expect(x => x.RedirectToLogin(null)).IgnoreArguments(); //act var attribute = new RequiresAuthenticationAttribute(mockedProvider); attribute.OnActionExecuting(null); //assert mockedProvider.VerifyAllExpectations(); } [Test] public void CanPassThroughWhenAuthenticated() { //arrange var mockedProvider = MockRepository.GenerateMock<IAuthenticationProvider>(); mockedProvider.Expect(x => x.IsAuthenticated(null)).IgnoreArguments().Return(true); //act var attribute = new RequiresAuthenticationAttribute(mockedProvider); attribute.OnActionExecuting(null); //assert mockedProvider.AssertWasNotCalled(x => x.RedirectToLogin(null)); mockedProvider.VerifyAllExpectations(); } } |
Using Rhino Mocks, I just mock the IAuthenticationProvider and pass it in to the constructor of my RequiresAuthenticationAttribute.
The code inside the FormsAuthenticationProvider relies on httpContext, which is damn near impossible to mock…I tried. So with this solution, I’ve at least isolated that code so that I can test everything else. I would LOVE to hear your thoughts on this…be brutal.
Update: The authentication section of Web.config indicates that the loginurl is Membership/Login….that means you’ll need a MembershipController with a Login action to display the login view, like this
1 2 3 4 5 6 7 | public class MembershipController: Controller { public ActionResult Login() { return View("Login"); } } |
Update: continue on to Part II, where we dig into the MembershipController
Recent Posts
Recent Comments
- Open Floor Plan vs. Private Offices « Step Into Design on It's Caves AND Commons...
- indomitablehef on Forms Authentication in Asp.Net MVC, Part II
- Dugald Wilson on Forms Authentication in Asp.Net MVC, Part II
- MyWeeklyLinks – Week 5 « Ole Morten Amundsen on Implementing Done, In Process, and Ready Queues in LeanKit Kanban
- indomitablehef on Schema Generation using FluentNHibernate and S#arp Architecture
Categories
- .Net (5)
- Agile (17)
- Alt.Net (3)
- Anti Patterns (3)
- Asp.Net MVC (9)
- Continuous Integration (4)
- Craftsmanship (1)
- CruiseControl.Net (1)
- DDD (6)
- DevLink (2)
- jQuery (2)
- Kanban (4)
- Lean (2)
- LeanKit (3)
- NAnt (2)
- NHibernate (2)
- ORM (1)
- Personal (4)
- Productivity (6)
- qUnit (2)
- Refactoring (1)
- S#arp Architecture (2)
- SOLID (1)
- SqlTdd (5)
- TDD (17)
- Tools (12)
- Uncategorized (11)
- Visual Studio (4)


