UPDATE 2: Improved IsAuthenticated helper method can be found here.

UPDATE: Fixed broken IsAuthenticated logic (thanks Travis).

Robert Dean published a great post last year about his Security Aware Html.ActionLink.  It allowed you to conditionally render links based on the [AuthorizeAttribute] assigned to a controller’s action methods.

Here’s an alternative implementation based on expressions, so you get type safety, intellisense and slightly simpler code :)  First, a helper to determine if an action is authorized:

public static class AuthorizationExtensions
{
    private static Dictionary<Expression, AuthorizeAttribute[]> expressionAuthorizers = new Dictionary<Expression, AuthorizeAttribute[]>();

    /// <summary>
    /// Determines whether the specified action is authorized.
    /// </summary>
    /// <typeparam name="TController">The type of the controller.</typeparam>
    /// <param name="helper">The helper.</param>
    /// <param name="actionMethod">The action method.</param>
    /// <returns>
    ///     <c>true</c> if the specified helper is authorized; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsAuthorized<TController>( this HtmlHelper helper, Expression<Action<TController>> action )
    {
        var call = action.Body as MethodCallExpression;

        if ( call == null ) return false;

        var authorizers = expressionAuthorizers.ContainsKey( action )
            ? expressionAuthorizers[ action ]
            : expressionAuthorizers[ action ] = GetAttributes<AuthorizeAttribute>( call );

        return ( authorizers.Length > 0 )
            ? authorizers.All( a => a.IsAuthorized( helper.ViewContext.HttpContext.User ) )
            : true;
    }

    /// <summary>
    /// Gets the specified attributes for an action method.
    /// </summary>
    /// <param name="call">The call.</param>
    /// <returns></returns>
    private static TAttribute[] GetAttributes<TAttribute>( MethodCallExpression call ) where TAttribute : Attribute
    {
        return call.Object.Type.GetCustomAttributes( typeof( TAttribute ), true )
            .Union( call.Method.GetCustomAttributes( typeof( TAttribute ), true ) )
            .Cast<TAttribute>()
            .ToArray();
    }

    /// <summary>
    /// Determines whether the specified <see cref="AuthorizeAttribute"/> authorizes the specified user.
    /// </summary>
    /// <param name="authorize">The <see cref="AuthorizeAttribute"/>.</param>
    /// <param name="user">The user.</param>
    /// <returns>
    ///     <c>true</c> if the specified user is authorized; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsAuthorized( this AuthorizeAttribute authorize, IPrincipal user )
    {
        if ( !user.Identity.IsAuthenticated ) return false;

        var users = authorize.Users.SplitString();
        if ( users.Length > 0 && !users.Contains( user.Identity.Name, StringComparer.OrdinalIgnoreCase ) ) return false;

        var roles = authorize.Roles.SplitString();
        if ( roles.Length > 0 && !roles.Any( user.IsInRole ) ) return false;

        return true;
    }

    /// <summary>
    /// Splits and trims the specified string.
    /// </summary>
    /// <param name="original">The original.</param>
    /// <returns></returns>
    public static string[] SplitString( this string original )
    {
        return string.IsNullOrEmpty( original )
            ? new string[ 0 ]
            : original.Split( '.' ).Select( s => s.Trim() ).Where( s => !string.IsNullOrEmpty( s ) ).ToArray();
    }
}

With that, it’s possible to write markup like this:

<% if ( Html.IsAuthorized<ModelingController>( c => c.Index() ) ) {%>
    <div>My conditional content...</div>
<%}%>
 

If you’re using ASP.NET MVC Futures, it’s straightforward to write an authorized ActionLink:

public static class LinkExtensions
{
    /// <summary>
    /// Render an HTML action link if authorized by the target action.
    /// </summary>
    /// <typeparam name="TController">The type of the controller.</typeparam>
    /// <param name="helper">The helper.</param>
    /// <param name="action">The action.</param>
    /// <param name="linkText">The link text.</param>
    /// <returns></returns>
    public static string AuthorizedActionLink<TController>( this HtmlHelper helper, Expression<Action<TController>> action, string linkText )
        where TController : Controller
    {
        return AuthorizedActionLink<TController>( helper, action, linkText, null );
    }

    /// <summary>
    /// Render an HTML action link if authorized by the target action.
    /// </summary>
    /// <typeparam name="TController">The type of the controller.</typeparam>
    /// <param name="helper">The helper.</param>
    /// <param name="action">The action.</param>
    /// <param name="linkText">The link text.</param>
    /// <param name="htmlAttributes">The HTML attributes.</param>
    /// <returns></returns>
    public static string AuthorizedActionLink<TController>( this HtmlHelper helper, Expression<Action<TController>> action, string linkText, object htmlAttributes )
        where TController : Controller
    {
        var routeValuesFromExpression = ExpressionHelper.GetRouteValuesFromExpression<TController>( action );

        return helper.IsAuthorized( action )
            ? helper.RouteLink( linkText, routeValuesFromExpression, new RouteValueDictionary( htmlAttributes ) )
            : null;
    }
}

NOTE – You could do the same thing without MVC Futures, but would need a little more code (you’d need something like ExpressionHelper’s GetRouteValuesFromExpression method).

Here’s a markup example:

<%= Html.AuthorizedActionLink<ModelingController>( c => c.Index(), "Modeling" ) %>
 
About these ads