Ricardo Sanchez
Coder, Photographer, Youtuber, Blogger

MVC

How to use Active Directory groups to restrict access to controller actions in ASP.NET MVC and make your application even more secure!

It’s been a year and one of the most popular posts in this blog still today is How To: Secure your ASP.NET MVC application and use Active Directory as the Membership Provider. In that post I promised to write about how to use Active Directory groups to restrict access to controller actions to make your application even more secure by consolidating access based in already defined groups in Active Directory (AD). I finally got to it and here it is.

Remember that if you are not already using Active Directory as your membership provider in your application, you need to first follow the steps described in the first post mentioned above.

In a business, the use of Active Directory to organize user and computer accounts is very common. When we create new web applications for that business it is likely that we want to have some access control to certain areas of the application. For example, let’s say that you have a web application that helps accounting and customer support get details about a certain customer such as reports, invoice details, account information, etc… In such application there is a good chance that accounting and customer service employees will have different access to different areas in the application.

The Example

Here is an example of what such a task will look like in the controller of your MVC application:

 public ActionResult DeactivateMembership(Membership model)
 {
    // your business logic here
    return View("DeactivateMembership", model);
 }

And here is what is going to look like with an attribute that will only allow users in the customer service group to execute such task

[AuthorizeAD(Groups = Constants.CSgroup)]
 public ActionResult DeactivateMembership(Membership model)
 {
    // your business logic here
    return View("DeactivateMembership", model);
 }

The custom AuthorizeAD attribute

The custom attribute labeled AuthorizeAD is what makes this happen, below is the declaration of this custom attribute that access Active Directory to determine if an specific user or group within AD has access to a defined controller action:

namespace Application.Filters
{
public class AuthorizeADAttribute : AuthorizeAttribute
{
    public string Groups { get; set; }
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (base.AuthorizeCore(httpContext))
        {
           /* Return true immediately if the authorization is not 
           locked down to any particular AD group */
           if (String.IsNullOrEmpty(Groups))
               return true;

           // Get the AD groups
           var groups = Groups.Split(',').ToList();

           // Verify that the user is in the given AD group (if any)
           var context = new PrincipalContext(
                                 ContextType.Domain, 
                                 "yourdomainname");

           var userPrincipal = UserPrincipal.FindByIdentity(
                                  context,
                                  IdentityType.SamAccountName,
                                  httpContext.User.Identity.Name);

           foreach (var group in groups)
           if (userPrincipal.IsMemberOf(context, 
                IdentityType.Name, 
                group))
               return true;
        }
         return false;
}

protected override void HandleUnauthorizedRequest(
AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
             var result = new ViewResult();
             result.ViewName = "NotAuthorized";
             result.MasterName = "_Layout";
             filterContext.Result = result;
        }
        else
             base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

The code above overrides the AuthorizeCore call which allow us to customize the authorization check so we can use the Active Directory in our domain.

The implementation

To limit access to controller actions you will use the new custom attribute like this:

[AuthorizeAD(Groups = Constants.CSgroup)]

Where Constant.CSGroup is just a constant value I created that translates to the actual name of the AD group. This can also be used to aggregate two or more AD groups as one value if needed. In the class below I set the value of CSgroup to be the name of two different AD groups in my domain, the csr group and csr_leads group:

public static class Constants
    {
        /// <summary>
        /// CS - Customer support and customer support leads
        /// </summary>
        public const string CSgroup = "csr, csr_leads";
}

If you don’t need to do this then you can use the custom attribute by simply providing the name of your AD group, a user name or the name of a Role within your Active Directory:

// Only give access to a group
[AuthorizeAD(Groups = "yourADgroup")]


// Give access to a group and a user
[AuthorizeAD(Groups = "yourADgroup", Users = "someuser")]

// Give access to a Role
[AuthorizeAD(Roles = "Admin")]
 

That’s it! Hope this is helpful for you and as always, if you have a recommendation, a comment or question please use the comment’s section below.

 

How To: Secure your ASP.NET MVC application and use Active Directory as the Membership Provider

Securing your ASP.NET MVC application should be priority number one every time you start a new web application. Using the attributes Authorize and ValidateAntiForgeryToken in every controller and action is the only way to avoid any security holes. In this post I’ll show you how to secure your ASP.NET application by implementing the AuthorizeAttribute and ValidateAntiForgeryTokenAttribute classes.

The basics

At the very least, you should add an [Authorize] attribute to every controller or controller Action in case you want some of the controller actions to be accessible by anonymous users. For example, you probably want ALL users to have access to the login and register actions of your web application.

By decorating the HomeController with the Authorize attribute (notice I did not specify any user role) the application will prevent any unauthenticated user from executing any of the actions in this controller.

[Authorize]
public class HomeController : Controller
{
  //...
}

The following is an example of decorating a controller action with the Authorize attribute, you want to do this when you only want to restrict access to some of the actions in a controller instead of all actions.

[Authorize]
public ActionResult Create()
{
  //...
}

Protecting against Cross-site request forgery attack (CSRF or XSRF)

The Authorize attribute offers protection that is sufficient in most cases. However, there is security hole with this and thus it opens your web application for a cross-site request forgery attack. For example, after a user logs into your site the website will issue your browser an authentication token within a cookie. Each subsequent request, the browser sends the cookie back to the site to let the site know that you are authorized to take whatever action you’re taking, so far everything is good.

Here is the problem with only using the Authorize attribute, let’s say that a user is logged in to your website and then they go to a spam site by clicking on a link that points to another site which causes a form post back to your site… this is bad, your browser will send the authentication cookie to your site making it appear as if the request came from your website and initiated by an authenticated user when it really didn’t.

The above scenario is called cross-site request forgery and can be avoided by adding the ValidateAntiForgeryToken attribute available in the .NET framework, this attribute is used to detect whether a server request has been tampered with.

The first step is to add the ValidateAntiForgeryToken attribute to every Post Action as follows:

[HttpPost, Authorize, ValidateAntiForgeryToken]
public ActionResult Create()
{
  //...
}

The next step is to add the HtmlHelper method @Html.AntiForgeryToken() inside the form in your view.

The way the ValidateAntiForgeryToken attribute works is by checking to see that the cookie and hidden form field left by the Html.AntiForgeryToken() HtmlHelper essentially exists and match. If they do not exist or match, it throws an HttpAntiForgeryException shown below:

“A required anti-forgery token was not supplied or was invalid”

By adding the ValidateAntiForgeryToken to your controller actions your site will be prepared to prevent CSRF/XSRF attacks.

Implementing Forms Authentication using Active Directory (AD)

Often times you might run across a project where you need to authenticate users of your website using Active Directory credentials, the good news is that you can use the existing “Account” controller to achieve this, only a few modifications are necessary.

When you create a new MVC Web Application project and choose the Internet Application template, the Account controller is added to the project, you can use this controller with AD to authenticate your users. For the Account controller to work with AD we need to remove all Actions but the following:

  • Logon()
  • Logon(LogOnModel model, string returnUrl)
  • LogOff()

Your Account controller should look like the following after you remove the unnecessary Actions such as ChangePassword, Register, etc…

public ActionResult LogOn()
        {
            return View();
        }
        
        [HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                if (Membership.ValidateUser(model.UserName, model.Password))
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                    if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                        && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\"))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {
                    ModelState.AddModelError("", "The user name or password provided is incorrect");
                }
            }

            // if we got this far, something failed, redisplay form
            return View(model);
        }

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();

            return RedirectToAction("Index", "Home");
        }

After this, go ahead and clean up the AccountModel as well so the only model class left is the LogOnModel:

public class LogOnModel
        {
            [Required]
            [Display(Name = "User name")]
            public string UserName { get; set; }

            [Required]
            [DataType(DataType.Password)]
            public string Password { get; set; }

            [Display(Name = "Remember me?")]
            public string RememberMe { get; set; }
        }

Lastly, add the following to the project’s web.config file:

adconnection

That is all! the first code snipet is the connectionstring to your Active Directory server and the second one is where we specify Active Directory as the application’s default membership provider.

Save your changes, hit Ctrl-F5 and logon to your application using your domain/AD account.

Hopefully this will help you get started to secure your ASP.NET web apps and show you a very simple way to use ASP.NET’s membership services with Active Directory.

In this post I show how to use Active Directory groups to restrict access to controller actions and make your application even more secure!

Retrieving images from a dabatase in ASP.NET MVC

If you run into a ASP.NET MVC site that needs to get images stored as binary data in a SQL database you can do the following to help you read these images and display them in your view.

The Controller

First, create a controller that will serve these images and add a void method that requires the image id as parameter to locate the right image to serve. In the example below I used the Adventure Works database and Linq to query the database and read an image stored in MS SQL. Make sure you set the content type to the right type of image file you’ll be getting from the database, in the example below I used “image/gif” as the content type.

The View

In your view, you only need to use the Url.Action helper to form the url to the image stored in the database by calling the method in the image controller you just created (see above).

“Show” is the name of our method and “Image” is the name of the controller, what’s on the right to “id=” is just the image id that I want to get.

 

 

 

 

Infinite scroll with ASP.NET MVC

An infinite scroll is a nice solution when you need to display large amounts of content in page, it helps by increasing performance in such a page because only a specific number of items is shown when the page first loads. As the user scrolls down, more content is shown. An infinite scroll is a better solution than having a paginated view of the page which usually breaks the flow of the page by splitting the content into multiple pages and then users have to click on a button or link to be able to see the next group of items…

I’ve used the following infinite scroll solution with ASP.NET MVC sites and it works great and it is simple to implement, all you need is jQuery and a little code in the controller and the view.

First, add a parameter to the controller action that returns the data for your page, this parameter is the one that we’ll use to specify the page number we need to get data for. The real work happens in the GetPaginatedProducts method, every time the user scrolls down the page this method is called by the controller action, we pass the page number and then use the Skip Linq command to get the next set of items.

The Controller

Here is the code we need in the controller for the infinite scroll to work:

The JavaScript

The following is the javascript needed to display a loading image and to initiate the call to the Product action on the HomeController, see below. Also, you will need to create a loading image to display while the application is getting data from the server, I used this site to create mine.

The View

Finally, in the view we make sure we have at least two divs, one with the id “productList” where the data is appended to when scrolling to the bottom of the page and another one with the id “loading” to use it to display the loading image:

Where is the sample project?

I’ve added the sample project to CodePlex, you can download it here: https://github.com/ricardodsanchez/InfiniteScroll