I have a question about custom authorization in MVC.
I have a site that I want to limit access to certain pages, depending on their group membership. Now I have seen tons of examples on how to do this if there is a single admin group and a single user group, for example, but not any examples for a third level.
For example, only users of a company can view orders for their own company (and each company has its own admins, etc). These companies are stored in a DB. So I have seen ways to do custom authorization, overriding the AuthorizeCore
method on the AuthorizeAttribute
, but I don't know how to access to the parameters passed into the controller to see if the user has access to the order (order id, for example).
Is this even the best place to do the check, or should this just be handled directly from the method of the controller?
Authorization in MVC is controlled through the AuthorizeAttribute attribute and its various parameters. At its simplest applying the AuthorizeAttribute attribute to a controller or action limits access to the controller or action to any authenticated user.
The AuthorizationContext (parameter to OnAuthorize) provides access to the Controller, RouteData, HttpContext, etc. You should be able to use these in a custom authorization filter to do what you want. Below is a sample of code from a RoleOrOwnerAttribute derived from AuthorizeAttribute.
public override void OnAuthorization( AuthorizationContext filterContext ) { if (filterContext == null) { throw new ArgumentNullException( "filterContext" ); } if (AuthorizeCore( filterContext.HttpContext )) // checks roles/users { SetCachePolicy( filterContext ); } else if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { // auth failed, redirect to login page filterContext.Result = new HttpUnauthorizedResult(); } // custom check for global role or ownership else if (filterContext.HttpContext.User.IsInRole( "SuperUser" ) || IsOwner( filterContext )) { SetCachePolicy( filterContext ); } else { ViewDataDictionary viewData = new ViewDataDictionary(); viewData.Add( "Message", "You do not have sufficient privileges for this operation." ); filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData }; } } // helper method to determine ownership, uses factory to get data context, // then check the specified route parameter (property on the attribute) // corresponds to the id of the current user in the database. private bool IsOwner( AuthorizationContext filterContext ) { using (IAuditableDataContextWrapper dc = this.ContextFactory.GetDataContextWrapper()) { int id = -1; if (filterContext.RouteData.Values.ContainsKey( this.RouteParameter )) { id = Convert.ToInt32( filterContext.RouteData.Values[this.RouteParameter] ); } string userName = filterContext.HttpContext.User.Identity.Name; return dc.Table<Participant>().Where( p => p.UserName == userName && p.ParticipantID == id ).Any(); } } protected void SetCachePolicy( AuthorizationContext filterContext ) { // ** IMPORTANT ** // Since we're performing authorization at the action level, the authorization code runs // after the output caching module. In the worst case this could allow an authorized user // to cause the page to be cached, then an unauthorized user would later be served the // cached page. We work around this by telling proxies not to cache the sensitive page, // then we hook our custom authorization code into the caching mechanism so that we have // the final say on whether a page should be served from the cache. HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache; cachePolicy.SetProxyMaxAge( new TimeSpan( 0 ) ); cachePolicy.AddValidationCallback( CacheValidateHandler, null /* data */); }
If the authorization is really that dynamic, I would handle it in the controller. I have one action where I do this - you can return a HttpUnauthorizedResult to redirect to the login page or you can show a custom error in your view.
I don't the default redirect to the login page when somebody is already logged in, but not in the correct role. That's very confusing for the user.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With