Category Archives: .NET

Codeplex Project for Dynamic Controller/Action Authorization in ASP.NET MVC

For those of you who used the code in my post here: http://www.ryanmwright.com/2010/04/25/dynamic-controlleraction-authorization-in-asp-net-mvc/

I have created a project on Codeplex for it (http://mvcauthorization.codeplex.com/). Note that this version has some major breaking changes from the code I posted in my previous post but has some major improvements, the two biggest being:

  • Provider based model so that a database provider is as simple as overriding two methods on AuthorizationProvider
  • A global action filter for authorization (requires DependencyResolver)

Check it out and let me know what you think. I will be adding a post to show how to integrate it with the MvcSitemap provider soon.

Integrating a Legacy DAL with WCF RIA services

You’ve gotta admit that RIA services is pretty awesome, it’s the perfect companion to Silverlight. However it’s relatively cumbersome and messy to get all the bells and whistles working with a legacy DAL (paging, sorting, etc) and in many cases it’s just not feasible to throw out your investment in your existing infrastructure. So what should you do? Well, I have started a project on Codeplex to address this issue (http://riatodal.codeplex.com/). Rather than go through the code now, I’ll post on what the framework accomplishes and give some code samples that use it. In subsequent posts I’ll delve into bits and pieces of it’s underpinnings.

First, imagine this scenario. You have a DAL and DAL providers. Specifically, let’s take a look at a user manager/provider like the ones shown below:

    public class UserManager
    {
        List<User> _users = new List<User>();

        public List<User> GetUsersByNameAndSearch(string name, int userStatusId, string whereClause, string orderBy, int pageIndex, int pageSize)
        {
            // Paginate our fake data source
            return _users.Skip(pageIndex * pageSize).Take(pageSize).ToList();
        }
    }

    public class DalProvider
    {
        private DalProvider()
        {
        }

        private static DalProvider _instance = null;
        public static DalProvider ProviderInstance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new DalProvider();
                }

                return _instance;
            }
        }

        private UserManager _umInstance = null;
        public UserManager UserManagerInstance
        {
            get
            {
                if (_umInstance == null)
                {
                    _umInstance = new UserManager();
                }

                return _umInstance;
            }
        }
    }

Pretty straight forward. There’s a provider following the singleton pattern, so to get a list of users, we would write something like:

List<User> users
= LegacyDal.DalProvider.ProviderInstance.UserManagerInstance.GetUsersByNameAndSearch(...);

and from there we can bind that list of users to a grid. But, that’s not very good for RIA services. Paging would become a nightmare (how do we get the skip/take parameters to our legacy DAL for paging?). And custom where clauses? eww! How do we link these two together and have them play nicely? The answer is to provide a framework similar to the one described below, which is composed of two primary things:

1) A LINQ provider which we can use to query our DAL
2) “Hints” on our RIA entities and domain service methods that will tell the LINQ provider how to resolve the appropriate DAL methods.

The best part is the solution will result in you do not having to modify anything in your DAL, it will just link up and work.

So how does it work? First, decorate your RIA entities that are exposed through your domain service with some custom attributes:

    [EntityMap(typeof(LegacyDal.Entities.User))]
    public class RIAUser
    {
        [Key]
        [FieldMap("UserName")]
        public string RiaUserName { get; set; }

        [FieldMap("FirstName")]
        public string RiaFirstName { get; set; }

        [FieldMap("UserStatus")]
        public string RiaUserStatus { get; set; }
    }

Pretty straightforward. Above we are mapping our RIA user to a user entity in our legacy DAL by mapping the entity type and it’s corresponding fields.

Then, we add some configuration:

  <riaToDalLinqSettings
     resolverType="Configuration"
     dalProviderInstance="LegacyDal.DalProvider.ProviderInstance, LegacyDal"
     entityManagerNameFormatString="{0}ManagerInstance"
    />

The above says we will resolve our DAL information using the current configuration by setting resolverType=”Configuration” (we can write our own and export it using MEF if we want, in which case we would set the resolverType=”Custom”). The dalProviderInstance string let’s the framework know the provider instance to use in the DAL. And for the entityManagerNameFormatString it’s just a format string which would evaluate to[EntityName]ManagerInstance, or “UserManagerInstance” if we were looking for a UserManager.

Then, our domain service looks like:

    [EnableClientAccess]
    public class GetDataService : LegacyDalDomainServiceBase
    {
        // Create the query context
        RiaToDalQueryContext<RIAUser> RIAUserQueryContext;

        public GetDataService()
        {
            // Instantiate the context with this domain service
            RIAUserQueryContext = new RiaToDalQueryContext<RIAUser>(this);
        }

        /// <summary>
        /// MethodMap will map our queryable GetUser() ria method
        /// to the GetUsersByNameAndSearch() method  in our DAL.
        /// It will look for an overload which takes name and userStatusId parameter.
        /// The full method signature in the legacy DAL looks like:
        ///     GetUsersByNameAndSearch(string name, int userStatusId,
        ///           string whereClause, string orderBy, int pageIndex, int pageSize)
        /// but by convention any parameter name of:
        ///       whereClause, orderBy, pageIndex, pageSize
        /// will be excluded from the match and
        /// will be passed data based on the LINQ query that is composed.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [MethodMap("GetUsersByNameAndSearch")]
        public IQueryable<RIAUser> GetUser(
            string name,
            int statusId
            )
        {
            // Use some LINQ against our RIA entities.
            // The Entities property should already filtered by the "name"
            // and statusId parameters of the current query method,
            // but we can do some additional filtering, and it will be propagated
            // through to the whereClause in the legacy DAL method.
            var query = from u in RIAUserQueryContext.Entities
                        where (u.RiaFirstName != "Jim" && u.RiaUserStatus == "Active") || u.RiaFirstName == "Ronnie"
                        orderby u.RiaFirstName descending
                        select u;

            // Return the query, let RIA services compose
            // any additional LINQ sent from the client
            // (such as any paging information)
            return query;
        }
    }

Woah! What’s this doing? I’ll let you figure that out from the comments and the code, but that’s all that needs to be done to integrate RIA with our legacy DAL, the framework will handle the rest. Here’s a screenshot of what the Legacy DAL method receives after this LINQ is executed:

and here’s what the client that executed the query method looked like:

Notice how the where clause from our LINQ expression got passed to the where clause parameter in our DAL method? (it even resolved the correct names by translating RIAFirstName to FirstName). The paging parameters came from the DataPager in the Silverlight client, as did the order by clause. The userName and userStatusId were received from the parameters in the IQueryable GetUser() domain service. All of this was done without any additional code, we just needed to give the framework some “hints” on how to resolve our DAL.

So what’s this running on? Just MEF, Silverlight 4 and WCF RIA Services. This is a very preliminary design, but I’m planning on adding:

  • Update support. Call this.Update(entity) in your domain service and based on the mappings it will persist the entity back to the DAL
  • Transaction support via TransactionScope
  • Deep loading with the [Associate] attribute
  • [Ignore] attributes for the query parameters so parameters can be selectively excluded from matching when trying to resolve the appropriate DAL method
  • Re-factoring the code and making it more plugable
  • Anything I can think of or that’s suggested to me!

Be sure to take a look on Codeplex for the source (and a working example), and if you have any suggestions please let me know in the comments section!

Dynamic Controller/Action Authorization in ASP.NET MVC

Note: This has a CodePlex project now: http://mvcauthorization.codeplex.com/. The code there is a little different than the code below, and has some nice new features. The code below should still serve as a good reference, however.

Often times, after you’ve authenticated your users, you’ll want to authorize what they actually have control over based on role. Typically in an ASP.NET MVC project this happens by using an authorize attribute not unlike the one shown below:

[Authorize(Roles = "Programmer, Manager")]
public ActionResult MyTopSecretActionForSuperCoolPeopleOnly()

Unfortunately, the above code directly ties your action and controller code to your user roles. So what happens if a new role is added? Well, in my fake system I just added one, and it’s called the Fonzie role. Anyone who is at Fonzie status is by definition super cool, and needs to have access to our super cool action. So our code above would become:

[Authorize(Roles = "Programmer, Manager, Fonzie")]
public ActionResult MyTopSecretActionForSuperCoolPeopleOnly()

But, because our solution is code based we need to wait for a re-deployment and only then will the Fonzies get access to to our action. Not good! Also, what do we do about any action links to that action? We don’t want them just showing up for any old user. We’d better address that as well.

The solution to this problem is to create new configuration sections in web.config, which will allow us to specify authorization roles for our actions and controllers in a declarative way. That way, we only need to update the configuration, and the roles are automatically mapped to the actions and controllers without need for re-deployment. Below is what the code using this functionality should ultimately look like.

Define a configuration for controller/action roles:

  <authorizationConfiguration>
    <controllerAuthorizationMappings>
      <add controller="Home" role="GeneralAccess">
        <!-- Define allowed roles for actions under the Home Controller -->
        <actionAuthorizationMappings>
          <add action="MyTopSecretActionForSuperCoolPeopleOnly" roles="Developer,Manager,Fonzie" />
        </actionAuthorizationMappings>
      </add>
    </controllerAuthorizationMappings>
  </authorizationConfiguration>

The configuration above says we should allow any user with GeneralAccess to the “Home” controller, but our top secret action should only be accessible to users with a Developer, Manager, or Fonzie role. Then to use it, we would just need to decorate our action with a new attribute called ActionAuthorize, which is derived from AuthorizeAttribute, and needs no parameters:

[ActionAuthorize]
public ActionResult MyTopSecretActionForSuperCoolPeopleOnly()

And the new attribute would be smart enough to look-up the roles for this action and allow/disallow access based on the configuration. (Note that in addition to [ActionAuthorize], we also have an attribute called [ControllerAuthorize] which does the same thing for controllers).

When we link to this action, we would need to have a custom html helper that only renders the action link when the user is in role for that action. The usage would look like:

<%= Html.SecuredActionLink("Secure Link.", "MyTopSecretActionForSuperCoolPeopleOnly") %>

To accomplish the above we will need to start off with five classes for our configuration section:

AuthorizationConfiguration
This class defines the root or our configuration section and contains a static accessor property, as well as a ControllerAuthorizationConfigurationCollection.

    public class AuthorizationConfiguration : ConfigurationSection
    {
        private static AuthorizationConfiguration _authorizationConfiguration
            = ConfigurationManager.GetSection("authorizationConfiguration") as AuthorizationConfiguration;

        public static AuthorizationConfiguration Section
        {
            get
            {
                return _authorizationConfiguration;
            }
        }

        [ConfigurationProperty("controllerAuthorizationMappings")]
        public ControllerAuthorizationConfigurationCollection ControllerAuthorizationMappings
        {
            get
            {
                return this["controllerAuthorizationMappings"] as ControllerAuthorizationConfigurationCollection;
            }
        }
    }

ControllerAuthorizationConfigurationCollection
This class will encapsulate our collection of Controller configurations.

   public class ControllerAuthorizationConfigurationCollection : ConfigurationElementCollection, 

IEnumerable<ControllerAuthorizationConfigurationElement>
    {
        /// <summary>
        ///
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public ControllerAuthorizationConfigurationElement this[int index]
        {
            get
            {
                return base.BaseGet(index) as ControllerAuthorizationConfigurationElement;
            }
            set
            {
                if (base.BaseGet(index) != null)
                {
                    base.BaseRemoveAt(index);
                }
                this.BaseAdd(index, value);
            }
        }

        /// <summary>
        ///
        /// </summary>
        public IEnumerable<ControllerAuthorizationConfigurationElement> Elements
        {
            get
            {
                for (int i = 0; i < base.Count; ++i)
                {
                    yield return this[i];
                }
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public new IEnumerator<ControllerAuthorizationConfigurationElement> GetEnumerator()
        {
            for (int i = 0; i < base.Count; ++i)
            {
                yield return this[i];
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        protected override ConfigurationElement CreateNewElement()
        {
            return new ControllerAuthorizationConfigurationElement();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((ControllerAuthorizationConfigurationElement)element).Controller;
        }
    }

ControllerAuthorizationConfigurationElement

This class will define the base configuration element for a controller. It has an optional property called “Roles” which we can use to lock down a controller by a certain set of roles. It also has an actionAuthorizationConfigurationCollection to maintain a list of actions.

    public class ControllerAuthorizationConfigurationElement : ConfigurationElement
    {
        [ConfigurationProperty("controller", IsRequired = true)]
        public string Controller
        {
            get
            {
                return (string)this["controller"];
            }
            set
            {
                this["controller"] = value;
            }
        }

        [ConfigurationProperty("roles", IsRequired = false)]
        public string Roles
        {
            get
            {
                return (string)this["roles"];
            }
            set
            {
                this["roles"] = value;
            }
        }

        [ConfigurationProperty("actionAuthorizationMappings")]
        public ActionAuthorizationConfigurationCollection ActionAuthorizationMappings
        {
            get
            {
                return this["actionAuthorizationMappings"] as ActionAuthorizationConfigurationCollection;
            }
        }
    }

ActionAuthorizationConfigurationCollection
This class will encapsulate our collection of Controller configurations under the current controller.

   public class ActionAuthorizationConfigurationCollection : ConfigurationElementCollection, 

IEnumerable<ActionAuthorizationConfigurationElement>
    {
        /// <summary>
        ///
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public ActionAuthorizationConfigurationElement this[int index]
        {
            get
            {
                return base.BaseGet(index) as ActionAuthorizationConfigurationElement;
            }
            set
            {
                if (base.BaseGet(index) != null)
                {
                    base.BaseRemoveAt(index);
                }
                this.BaseAdd(index, value);
            }
        }

        /// <summary>
        ///
        /// </summary>
        public IEnumerable<ActionAuthorizationConfigurationElement> Elements
        {
            get
            {
                for (int i = 0; i < base.Count; ++i)
                {
                    yield return this[i];
                }
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public new IEnumerator<ActionAuthorizationConfigurationElement> GetEnumerator()
        {
            for (int i = 0; i < base.Count; ++i)
            {
                yield return this[i];
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        protected override ConfigurationElement CreateNewElement()
        {
            return new ActionAuthorizationConfigurationElement();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((ActionAuthorizationConfigurationElement)element).Action;
        }
    }

ActionAuthorizationConfigurationElement
This class will encapsulate a particular action under a controller. It has a required “Roles” parameter.

    public class ActionAuthorizationConfigurationElement : ConfigurationElement
    {
        [ConfigurationProperty("action", IsRequired = true)]
        public string Action
        {
            get
            {
                return (string)this["action"];
            }
            set
            {
                this["action"] = value;
            }
        }

        [ConfigurationProperty("roles", IsRequired = true)]
        public string Roles
        {
            get
            {
                return (string)this["roles"];
            }
            set
            {
                this["roles"] = value;
            }
        }
    }

After we have defined our configuration elements and registered the section in our web.config, we can start using it. We define two attribute classes which will perform the authentication look-up for our controllers and actions:

ActionAuthorizeAttribute targets an action method:

    /// <summary>
    /// Authorization attribute for an MVC action method
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class ActionAuthorizeAttribute : AuthorizeAttribute
    {
        private string[] _rolesArray;
        private string _authorizationGroupName;

        /// <summary>
        ///
        /// </summary>
        public string AccessDeniedController { get; set; }

        /// <summary>
        ///
        /// </summary>
        public string AccessDeniedAction { get; set; }

        /// <summary>
        ///
        /// </summary>
        public ActionAuthorizeAttribute()
        {
            AccessDeniedController = ConfigurationManager.AppSettings["DefaultAccessDeniedController"] ?? 

"AccessDenied";
            AccessDeniedAction = ConfigurationManager.AppSettings["DefaultAccessDeniedAction"] ?? "Index";
            _authorizationGroupName = string.Empty;
        }

        /// <summary>
        ///
        /// </summary>
        public string[] RolesArray
        {
            get
            {
                return _rolesArray;
            }
            set
            {
                _rolesArray = value;
                this.Roles = string.Join(",", _rolesArray);
            }
        }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;

            // Get any controller specific role mappings
            var controllerRoleMappings = AuthorizationConfiguration.Section.ControllerAuthorizationMappings.Where(e 

=> e.Controller == controllerName).FirstOrDefault();

            if (controllerRoleMappings != null)
            {
                var actionRoleMappings = controllerRoleMappings.ActionAuthorizationMappings.Where(e => e.Action == 

filterContext.ActionDescriptor.ActionName).FirstOrDefault();

                if (actionRoleMappings != null && !string.IsNullOrEmpty(actionRoleMappings.Roles))
                {
                    this.RolesArray = actionRoleMappings.Roles.Split(',');
                }
            }

            //
            base.OnAuthorization(filterContext);
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { 

Controller = AccessDeniedController, Action = AccessDeniedAction, Roles = Roles }));
        }
    }

ControllerAuthorizeAttribute targets a controller class:

    /// <summary>
    /// Authorization attribute for an MVC controller
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class ControllerAuthorizeAttribute : AuthorizeAttribute
    {
        private string[] _rolesArray;
        private string _authorizationGroupName;

        /// <summary>
        ///
        /// </summary>
        public string AccessDeniedController { get; set; }

        /// <summary>
        ///
        /// </summary>
        public string AccessDeniedAction { get; set; }

        /// <summary>
        ///
        /// </summary>
        public ControllerAuthorizeAttribute()
        {
            AccessDeniedController = ConfigurationManager.AppSettings["DefaultAccessDeniedController"] ?? 

"AccessDenied";
            AccessDeniedAction = ConfigurationManager.AppSettings["DefaultAccessDeniedAction"] ?? "Index";
            _authorizationGroupName = string.Empty;
        }

        /// <summary>
        ///
        /// </summary>
        public string[] RolesArray
        {
            get
            {
                return _rolesArray;
            }
            set
            {
                _rolesArray = value;
                this.Roles = string.Join(",", _rolesArray);
            }
        }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;

            // Get any controller specific role mappings
            var controllerRoleMappings = AuthorizationConfiguration.Section.ControllerAuthorizationMappings.Where(e 

=> e.Controller == controllerName).FirstOrDefault();

            if (controllerRoleMappings != null && !string.IsNullOrEmpty(controllerRoleMappings.Roles))
            {
                this.RolesArray = controllerRoleMappings.Roles.Split(',');
            }

            //
            base.OnAuthorization(filterContext);
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { 

Controller = AccessDeniedController, Action = AccessDeniedAction, Roles = Roles }));
        }
    }

Then for dynamic action link rendering based on role, the following Html helper would be as follows:


        /// <summary>
        /// Renders the action link, provided the user has access to the action method being linked to
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="linkText"></param>
        /// <param name="actionName"></param>
        /// <returns></returns>
        public static MvcHtmlString SecuredActionLink(this HtmlHelper helper, string linkText, string actionName)
        {
            MvcHtmlString html = MvcHtmlString.Empty;
            string[] roles = null;

            RouteData routeData = helper.ViewContext.RouteData;
            string controllerName = routeData.GetRequiredString("controller");

            // Get any controller specific role mappings
            var controllerRoleMappings = AuthorizationConfiguration.Section.ControllerAuthorizationMappings.Where(e 

=> e.Controller == controllerName).FirstOrDefault();
            ActionAuthorizationConfigurationElement actionRoleMappings = null;

            if (controllerRoleMappings != null)
            {
                actionRoleMappings = controllerRoleMappings.ActionAuthorizationMappings.Where(e => e.Action == 

actionName).FirstOrDefault();

                if (actionRoleMappings != null && !string.IsNullOrEmpty(actionRoleMappings.Roles))
                {
                    roles = actionRoleMappings.Roles.Split(',');
                }
            }

            if (roles != null)
            {
                foreach (string role in roles)
                {
                    if (ApplicationUser.Current.Roles.Contains(role))
                    {
                        html = System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, linkText, actionName);
                        break;
                    }
                }
            }
            else if (actionRoleMappings == null)
            {
                // If there's no roles associated to that action then render the link
                html = System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, linkText, actionName);
            }

            return html;
        }
    }

The helper above will figure out if the user has access to the action, display accordingly, and viola! Users will never see a link to an action for which they do not have access, and if they do somehow get to an action that is restricted based on their role they will get redirected to an access denied page of your choosing.

This is just a basic sample, but there’s lots that can be done in addition to this. Be sure to download the source here to see it in action (VS 2010, ASP.NET MVC 2, and WIF required). In particular play around with the role definitions at the bottom of the web.config to see the actions dynamically authorize as the roles in the config change. The authentication is done via the default STS server project that gets created when you create a federated security application, so you will need to install the WIF RTW and SDK in order to successfully run the project the way it is.

Switch to our mobile site