dannit dannit - 3 days ago 5
C# Question

ASP.NET MVC5 - Override OnAuthorization() - 'MvcResources' is inaccessible due to its protection level

The main goal: I am creating a website (ASP.NET MVC 5) where I need to add some additional Authorization and redirection logic: For example, a user can only view page C after completing pages A and B. If they have completed page A and not B, and try to access page C, they will be redirected to page B.

After a good amount of research, my plan is to create a custom AuthorizationAttribute , and override OnAuthorization(). I want to do this responsibly, so I looked at the [source code][1] for this method, and want to only add logic to it, not take it away. I've started by copying that code over to my own subclass (which I include at the end of this post):

The problem is there are two elements here that I apparently cannot access:


  • MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache

  • CacheValidateHandler



The first is in System.Web.Mvc.Properties , and the error I get is that 'MvcResources is inaccessible due to its protection level'. All of the help I've seen online regarding this advises that the programmer change the access modifier for their class, but I can't since I didn't write this class: it's system code.

The second (CacheValidateHandler) 'does not exist in the current context'. It's a method in my parent class (AuthorizeAttribute), but it's private.

So is there something I'm missing? Does my subclass have to be in a special location (right now it's in a folder called Helpers) or do I have to do something different with namespaces? I'm still pretty new to C#. How can I safely override OnAuthorize if I'm not even able to repeat what the parent method does?

namespace MyApp.Helpers
{
public class MyAppAuth : AuthorizeAttribute
{
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}

if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
{
// If a child action cache block is active, we need to fail immediately, even if authorization
// would have succeeded. The reason is that there's no way to hook a callback to rerun
// authorization before the fragment is served from the cache, so we can't guarantee that this
// filter will be re-run on subsequent requests.
throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
}

bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);

if (skipAuthorization)
{
return;
}

if (AuthorizeCore(filterContext.HttpContext))
{
// ** 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 */);
}
else
{
HandleUnauthorizedRequest(filterContext);
}
}
}
}

Answer

I believe I have a solution, but I would really love if someone with more experience could comment if what I'm doing is in any way unsafe.

For the first issue, I simply replaced MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache with a string, since this is one of the possible overloads of InvalidOperationException.

For the second issue, I copy-pasted the parent's private CacheValidateHandler() method:

private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
    validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
Comments