Monday, February 10, 2014

MVC Filters Part 1 - Authorization Filter


Filters - By Narendra Shrestha

Filters are one of the elegant features available in .net framework. They are one of the excellent examples of cross-cutting concern where entity or functionality are scattered all over the application and doesn’t fit neatly in one place. Filter makes writing MVC application becomes much easier and lot cleaner. We must have all used filters in the MVC framework at some point. To recall one, for example “Authorize” filter, which is applied to actions or controller, to guarantee secure access to the limited users. One more advantage of filter is the elimination of the code duplication. For example the below actions in “Admin” controller are absolutely fine.

public ActionResult ShowActiveUsers()
{
    if (Request.IsAuthenticated && User.Identity.IsAuthenticated)
    {
        return View();
    }
    return View("UnAuthorized");
}

public ActionResult EditAccounts()
{
    if (Request.IsAuthenticated && User.Identity.IsAuthenticated)
    {
        return View();
    }
    return View("UnAuthorized");
}

Listing 1:-Typical “admin” controller without “Authorize” filters

In above listing, typical actions most likely to be found within “Admin” controller are anticipated. These actions include displaying the list of active users or more precisely preventing the unknown users from critical admin functionalities. The same effect can be achieved by implementing “Authorize” filter which is a .net attribute filter. The code implementing this filter is listed as below:-

[Authorize]
public ActionResult ShowActiveUsers()
{
    return View();
}

[Authorize]
public ActionResult EditAccounts()
{
    return View();
}

Listing 2:- Elimination of code duplication after application of “Authorize” filter

Needless to mention again, how much neater the code looks in listing 2 in comparison to listing 1. How many of you have noticed sudden disappearance of concern for the view “Unathorized”. It’s the beauty of the “Authorize” filter. The failed authorization is handled in the “Web.config” file of the MVC application.

<authentication mode="Forms">
      <forms loginUrl="~/Home/UnAuthorized" timeout="2880" />
</authentication>

Listing 3:-Specifying the URL to redirect in case of unauthorized access in “Web.Config”.

Whenever, there is an unauthorized access, the user will be prompted with “Unauthorized” page to provide valid credentials.

More commonly, filters are .net attributes that add extra information to the request processing pipeline.

What are attributes then?

The attributes are .net class derived from the namespace “System.Attribute”. By deriving from “Attribute” class you can create our own attributes. On top of this, you can declare your own properties, methods and fields within your class. These properties convey information during the runtime. While creating, make sure that the term “Attribute” is appended to the name of your class. It’s a compulsory naming convention for the custom attributes. For example “AuthorizeAttribute” is the name of the authorization filter attribute in the MVC framework. However, when using this filter, we simply tend to use “[Authorize]”. Attributes is one of the efficient technique in .net to improvise filters.

Types of filters in MVC

MVC Framework supports four different kinds of filters shown below:-

Filter
Interface
Default implementation
Roles
Authorization
IAuthorizationFilter
AuthorizeAttribute

Runs first and before any other filters or action methods
Action
IActionFilter

ActionFilterAttribute

Runs before and after the execution of the action method
Results
IResultFilter

ActionFilterAttribute

Runs before and after the action result is executed
Exception
IExceptionFilter

HandleErrorAttribute

Runs when the exception is thrown from other filter, action methods or action results.

Table 1:-Filters in MVC

Authorization Filter

As mentioned earlier, this filter runs prior to any other filters and action methods. This filter is intended for the authorization policy and restricts unauthorized users from secured methods. To implement your own custom authorization filter, you need to implement the interface “IAuthorizationFilter”. The following code lists the skeletal code for this interface.

using System;

namespace System.Web.Mvc
{
    // Summary:
    //     Defines the methods that are required for an authorization filter.
    public interface IAuthorizationFilter
    {
        // Summary:
        //     Called when authorization is required.
        //
        // Parameters:
        //   filterContext:
        //     The filter context.
        void OnAuthorization(AuthorizationContext filterContext);
    }
}

Listing 4:-Skeletal code for the interface “IAuthorizationFilter

However, you will barely find yourself in such need. The default authorization filter available in MVC framework is fully capable of ensuring safe authorization policies. These default authorization filters are thoroughly inspected and tested by the Microsoft. There’s no harm implementing your own, but there comes risk when you do so. An untested corner or any undetected loophole in your custom authorization policies can cost you unprecedented damage. Also, you are on safer side with default implementation. At least you can be relaxed with the fact that the Microsoft will be responsible for loopholes.

Instead of implementing “IAuthorizationFilter”, the much elegant method is to derive from “AuthorizeAttribute”. This way we can be sure that we do not fiddle with default behavior of the authorization filter. We create our own authorization attribute class called “MyAuthorizationAttribute”, derive it from “AuthorizeAttribute” and place it within “Infrastructure” folder.

The behavior required is to provide secure access to only unblocked IP addresses. It’s more likely to be implemented on the intranet applications where all systems are interlinked under the same subnet mask. The code for this custom authorization filter is:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Filters.Infrastructure
{
    public class MyAuthorizationAttribute : AuthorizeAttribute
    {
        private List<string> blockedIps;
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            LoadBlockIpAddresses();
            return (!blockedIps.Contains(httpContext.Request.UserHostAddress));
        }
        public void LoadBlockIpAddresses()
        {
            blockedIps = new List<string>();
            blockedIps.Add("127.0.0.2");
        }
    }
}

Listing 5:- “MyAuthorizationAttribute” class

The list of the block IP addresses are maintained through method “LoadBlockIpAddresses” and string list “blockedIps”. If the client’s IP address is not contained within blocked list, he/she has access. The “AuthorizeCore” method of the “AuthorizeAttribute” class is the one to be overridden. The parameter to this method is of type “HttpContextBase” giving access to various request and response variables. Be sure to keep your default route as “Home” controller and “Index” action

In the “Home” controller, there are two action method listed as below

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Filters.Infrastructure;

namespace Filters.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/
        [MyAuthorization]
        public ActionResult Index()
        {
            return View();
        }
        public ActionResult UnAuthorized()
        {
            return View("Unauthorized");
        }
    }
}

Listing 6:-“Home” controller

As discussed previously, action “UnAuthorized” is meant to handle the request for unauthorized access. In our case it simply renders the view named “Unauthorized”. For unauthorized access, the URL pointing to this action has been specified in the “authentication” tag of the “Web.config” configuration.

The most important thing to take into account is the “Index” action where custom filter attribute is applied i.e. “[MyAuthorization]”. Thus, when we access the URL /Home/Index, the custom filter “MyAuthorizationAttribute” runs first and return the Boolean value based on whether the user’s IP address is blocked or not. In order to use “[MyAuthorization]” attribute, the namespace “Filters.Infrastructure” must be bounded. The views incorporated are “Index.cshtml” for “Index” action and “Unathorized.cshtml” for “Unathorized” action. However, the view for “Unathorized” action is placed within “Shared” folder because there is no strict distinction regarding its origin or placement.
Both of these views are listed as below.

@{
    ViewBag.Title = "Index";
}

<h2>Your computer has access</h2>

Listing 7:-“Index.cshtml”


@{
    ViewBag.Title = "Unauthorized";
}

<h2>You are not authorized</h2>

Listing 8:-“Unathorized.cshtml”


Before running the project, you need to make note of your system’s IP address. By adding any other IP addresses to the blocked IP addresses list, and redirecting to URL “Home/Index” generates following output.


Figure 1:- When user’s IP address isn’t included in the blocked list (user has access)

Next include your own IP address within the blocked list. This should prevent you from accessing index action and you will be taken to “Unathorized” view as shown below.

 
Figure 2:-When user’s IP address is listed in the blocked list (user has no access)

One of the prime reasons for the developers to write their own authorization policy is because of “AuthorizationContext” parmeter within the “OnAuthorization” method of interface “IAuthorizationFilter”. Since “AuthorizationContext” is derived from “ControllerContext”, we get access to route data, controller specific properties etc within this method. This tempts developers to use their own authorization policies.

Default Authorization Filter

Default Authorization filter can fulfill our requirement and we don’t even need to bother about any custom implementation. Referring table 1, “AuthorizeAttribute” is the default implementation of the authorization filter. “AuthorizeAttribute” filter has its own properties which are most like to be used. They are:-
·         Users” of type “string” used to list the name of the permitted users in comma separated format
·         Roles” of type “string” used to list the permitted roles of the users in comma separated format

To be able to use roles and authentication, we need to make some changes in our “Web.config” configuration file as listed below.

<authentication mode="Forms">
  <forms loginUrl="~/Home/UnAuthorized" timeout="2880">
    <credentials>
      <user name="name" password="name"/>
    </credentials>
  </forms>
</authentication>
<roleManager enabled="true" cacheRolesInCookie="true" />

Listing 9:-changes in “Web.config” file for authentication and role.

For simplicity we added “credentials” tag and hard-coded the username and password. I.e. name as “name” and password as “name”. Similarly, we enabled role manager inside “roleManager” tag. We agreed to cache our roles in cookies by setting “cacheRolesInCookie” to true. In real scenarios, for role management you will be using advanced role providers like SQL role provider.

Our focal point at this moment is on the action “AccessSafe” in “Home” controller. In order to be able to access this action, the user must be logged in and must belong to “admin” role. We made following changes inside the “Home” controller:-

public ActionResult Index()
{
    //if (!Roles.RoleExists("admin"))
    //{
    //    Roles.CreateRole("admin");
    //    Roles.AddUsersToRole(new[] { "name" }, "admin");
    //}
    //FormsAuthentication.Authenticate("name", "name");
    //FormsAuthentication.SetAuthCookie("name", false);
    return View();
}
public ActionResult UnAuthorized()
{
    return View("Unauthorized");
}
[Authorize(Users="name",Roles="admin")]
public string AccessSafe()
{
    return "safe accessed successfully!!!";
}

Listing 10:-Changes made to “Home” controller

First of all “[MyAuthorization]” custom attribute has been removed from the Index action to avoid confusion. The new action “AccessSafe” is added and is included with attribute “AuthorizeAttribute”.
The values of the authorize attribute properties are set as named parameters. The allowed usernames are set to “name” and roles are set to “admin”. Run the project and visit the URL “/Home/AccessSafe”. We will be shown following output:-


Fig 3:-Unauthorized access of action “AccessSafe” within “Home” controller

When we access this action without authentication, the default authorization filter prevents us from doing so. Instead we are taken to URL “/Home/UnAuthorized” as stated in the configuration file.

Uncomment the code of the “Index” action in listing 10. We used “Roles” static class from namespace “System.Web.Security” to work with roles. First of all, we check if the role “admin” exists. If it doesn’t we add this particular role to the list.  Then we bind username “name” with the role “admin” by using “AddUsersToRole” method. Remember these roles are cached in cookies because of our setting in the configuration file.

FormsAuthentication” static class also belongs to namespace “System.Web.Security”. “Authenticate” method of static is used in order to authenticate users. The username and password are hard-coded here. So we don’t require authentication result returned by method “Authenticate” which is of Boolean type. After all we know that the authentication will succeed. In ideal situation we may be retrieving these data from model binder along with POST request. The authentication is persisted through cookie by method “SetAuthCookie”.

Since “Index” action of “home” controller is default route in the route table, we can be sure that we will be authenticated as soon as our project runs. When “Index” view has completely loaded, we can redirect to URL “/Home/AccessSafe”. We will see the following output:-


Figure 4:- Authorized to access the action “AccessSafe” within “Home” controller

In an actual case, the default authorization filter is used to restrict the user based on their roles. For example “Guest” will have very limited access, “User” will have fair access and “admin” will have unlimited access. Although the idea of custom authorization filter sounds tempting, we shouldn’t attempt something like that until we are very determined or have no choice.

No comments:

Post a Comment