Categories
ASP.NET ASP.NET MVC Microsoft

Create an authorized action link extension for ASP.NET MVC 3

In this article I will explain how to create an action link extension that is authorizations aware, so that we can hide or disable an action link based on its authorizations.

When you create a website with multiple permissions, it would be nice to display action links depending on those permissions. For example if your website contains a menu, its better to display links according to the permissions. You don’t want your users to click on a link, and then display an unauthorized access message.

 

With ASP.NET MVC, we can easily create an action link that is authorizations aware. It will be above the standard action link.

Important: note that the code below is only for ASP.NET MVC 3. It’s because there has been some changes with filters since ASP.NET MVC 2.

 

 Creation

First we will create an extension that will allow us to know if an action is authorized:

using System.Web.Mvc;

namespace MvcApplication.AuthorizedActionLink.Extensions
{
	public static class ActionExtensions
	{
		public static bool ActionAuthorized(this HtmlHelper htmlHelper, string actionName, string controllerName)
		{
			ControllerBase controllerBase = string.IsNullOrEmpty(controllerName) ? htmlHelper.ViewContext.Controller : htmlHelper.GetControllerByName(controllerName);
			ControllerContext controllerContext = new ControllerContext(htmlHelper.ViewContext.RequestContext, controllerBase);
			ControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(controllerContext.Controller.GetType());
			ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);

			if (actionDescriptor == null)
				return false;

			FilterInfo filters = new FilterInfo(FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor));

			AuthorizationContext authorizationContext = new AuthorizationContext(controllerContext, actionDescriptor);
			foreach (IAuthorizationFilter authorizationFilter in filters.AuthorizationFilters)
			{
				authorizationFilter.OnAuthorization(authorizationContext);
				if (authorizationContext.Result != null)
					return false;
			}
			return true;
		}
	}
}

As you can see we are retrieving action authorization filters. For each filter we test if the authorization context Result property is null. If this property is not null, it means that the user is not authorized to go on the action.

 

We also use an extension named GetControllerByName, in case the controller name is not specified:

using System;
using System.Globalization;
using System.Web.Mvc;

namespace MvcApplication.AuthorizedActionLink
{
	internal static class Helpers
	{
		public static ControllerBase GetControllerByName(this HtmlHelper htmlHelper, string controllerName)
		{
			IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
			IController controller = factory.CreateController(htmlHelper.ViewContext.RequestContext, controllerName);
			if (controller == null)
			{
				throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "The IControllerFactory '{0}' did not return a controller for the name '{1}'.", factory.GetType(), controllerName));
			}
			return (ControllerBase)controller;
		}
	}
}

 

Now that we are able to know if an action is authorized, we can create a new link extension named ActionLinkAuthorized so it sticks to the standard ActionLink:

using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
using MvcApplication.AuthorizedActionLink.Extensions;

namespace MvcApplication.AuthorizedActionLink.Html
{
	public static class LinkExtensions
	{
		public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string linkText, string actionName, bool showActionLinkAsDisabled = false)
		{
			return htmlHelper.ActionLinkAuthorized(linkText, actionName, null, new RouteValueDictionary(), new RouteValueDictionary(), showActionLinkAsDisabled);
		}

		public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, bool showActionLinkAsDisabled = false)
		{
			return htmlHelper.ActionLinkAuthorized(linkText, actionName, null, new RouteValueDictionary(routeValues), new RouteValueDictionary(), showActionLinkAsDisabled);
		}

		public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, bool showActionLinkAsDisabled = false)
		{
			return htmlHelper.ActionLinkAuthorized(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary(), showActionLinkAsDisabled);
		}

		public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues, bool showActionLinkAsDisabled = false)
		{
			return htmlHelper.ActionLinkAuthorized(linkText, actionName, null, routeValues, new RouteValueDictionary(), showActionLinkAsDisabled);
		}

		public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, object htmlAttributes, bool showActionLinkAsDisabled = false)
		{
			return htmlHelper.ActionLinkAuthorized(linkText, actionName, null, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes), showActionLinkAsDisabled);
		}

		public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool showActionLinkAsDisabled = false)
		{
			return htmlHelper.ActionLinkAuthorized(linkText, actionName, null, routeValues, htmlAttributes, showActionLinkAsDisabled);
		}

		public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes, bool showActionLinkAsDisabled = false)
		{
			return htmlHelper.ActionLinkAuthorized(linkText, actionName, controllerName, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes), showActionLinkAsDisabled);
		}

		public static MvcHtmlString ActionLinkAuthorized(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool showActionLinkAsDisabled)
		{
			if (htmlHelper.ActionAuthorized(actionName, controllerName))
			{
				return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes);
			}
			else
			{
				if (showActionLinkAsDisabled)
				{
					TagBuilder tagBuilder = new TagBuilder("span");
					tagBuilder.InnerHtml = linkText;
					return MvcHtmlString.Create(tagBuilder.ToString());
				}
				else
				{
					return MvcHtmlString.Empty;
				}
			}
		}
	}
}

 

Example of use

Let’s say we want to authorize only users with role Administrator on the HomeController action named ThePrivilegeZone. ThePrivilegeZone action will be decorated with an authorize attribute. We add a link to this action in the site menu with the authorized action link extension specifying that we want to show the  action link as disabled if the user is not authorized:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
<%@ Import Namespace="MvcApplication.AuthorizedActionLink.Html" %>

<!DOCTYPE html>
<html>
<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="<%: Url.Content("~/Scripts/jquery-1.4.4.min.js") %>" type="text/javascript"></script>
</head>

<body>
    <div class="page">

        <div id="header">
            <div id="title">
                <h1>My MVC Application</h1>
            </div>
              
            <div id="logindisplay">
                <% Html.RenderPartial("LogOnUserControl"); %>
            </div> 
            
            <div id="menucontainer">
            
                <ul id="menu">              
                    <li><%: Html.ActionLink("Home", "Index", "Home")%></li>
                    <li><%: Html.ActionLink("About", "About", "Home")%></li>
                    <li><%: Html.ActionLinkAuthorized("The Privilege Zone", "ThePrivilegeZone", "Home", true)%></li>
                </ul>
            
            </div>
        </div>

        <div id="main">
            <asp:ContentPlaceHolder ID="MainContent" runat="server" />

            <div id="footer">
            </div>
        </div>
    </div>
</body>
</html>

 

The result looks like below when the user is not authorized and the action link disabled:

It would be better to add some styling 🙂

 

To go further

To be honest I didn’t invent anything here. I have just take a deep look into the ASP.NET MVC 3 source code available on the Microsoft website in order to understand how authorizations works, and I have mixed it with the standard ActionLink. My code is based on three classes: ControllerActionInvoker, MvcHandler, LinkExtensions.

 

Summary

We have seen how to create and use an authorized action link.

You can download the example solution here:

Download full sources

(Note that the project uses ASP.NET MVC 3)

 

Please feel free to comment or contact me if you have any question about this article.

Categories
Microsoft SharePoint 2010

SharePoint Foundation or Server 2010: Error when you are trying to get an SPListItem value

You get a ‘System.ArgumentException: Value does not fall within the expected range” when you try to get the value of an SPListItem. Here is one solution to your problem!

This article will maybe help out some folks, because I didn’t find the answer elsewhere.

I wanted to retrieve an SPListItem value, and I got the following error:

System.ArgumentException: Value does not fall within the expected range.
at Microsoft.SharePoint.SPFieldMap.GetColumnNumber(String strFieldName, Boolean bThrow)
at Microsoft.SharePoint.SPListItemCollection.GetColumnNumber(String groupName, Boolean bThrowException)
at Microsoft.SharePoint.SPListItemCollection.GetRawValue(String fieldname, Int32 iIndex, Boolean bThrow)
at Microsoft.SharePoint.SPListItem.GetValue(SPField fld, Int32 columnNumber, Boolean bRaw, Boolean bThrowException)
at Microsoft.SharePoint.SPListItem.get_Item(Guid fieldId)

First, I didn’t understand why this error was thrown on a such basic operation in SharePoint development.

Then going step by step trying to find a solution, I noticed that on my current SPList I had several field of type “Lookup” and “Person or Group”, eleven exactly.
 

I finally found the solution in the Web Application settings.

Go to the Central Administration > Manage web applications > General Settings > Resource Throttling.

Then take a look to the option named List View Lookup Threshold.

The description says: Specify the maximum number of Lookup, Person/Group, or workflow status fields that a database query can involve at one time.

This option is the solution to our problem!

By default the value is set to 8. Just modify it depending on your needs. For my part I set it to eleven, and the error didn’t show up again.

Just be carefull with this option, because it is used for performance reason. Don’t raise it too much, just modify it to suit your needs.

 

Please feel free to comment or contact me if you have any question about this article.