Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting if AuthorizationAttribute manually called

I have a custom AuthorizeAttribute in a legacy MVC5 project:

public class AuthorizeWithLoggingAttribute : AuthorizeAttribute
{
     public override void OnAuthorization(AuthorizationContext filterContext)
     {
         if (!base.AuthorizeCore(httpContext)) {Log(FilterContext);}
     }
 }

We noticed while looking through the logs, that in addition to being to applied to controllers with [AuthorizeWithLogging], it was being called explicitly elsewhere in the code, generating spurious logs:

var filters = new FilterInfo(FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor));
foreach (var authFilter in filters.AuthorizationFilters)
{
    authFilter.OnAuthorization(authContext);
    if (authContext.Result != null) {return false;}
}

Is there a way to tell (via StackTrace or something) whether the OnAuthorization method is being explicitly called, or called from the attribute? The best I currently have is Environment.StackTrace.Contains("at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters").

like image 782
Arithmomaniac Avatar asked Jun 03 '16 15:06

Arithmomaniac


2 Answers

AuthorizeAttribute has a single responsibility: to determine whether or not the user is authorized. This can be used in multiple places in the application for a variety of different reasons.

Any actions that are taken as a result of not being authorized (such as returning a HTTP 401 response) are delegated to a handler of type ActionResult that is set to the AuthorizationContext.Result property. For example, here is the default implementation of AuthorizeAttribute.HandleUnauthorizedRequest:

protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
    filterContext.Result = new HttpUnauthorizedResult();
}

If you are trying to do auditing when a user is not authorized, you should put the auditing into the ActionResult handler, not in the custom AuthorizeAttribute. This ensures the auditing is only executed if the ActionResult is executed (that is, when the current page is not authorized), not in every case authorization is checked.

public class AuthorizeWithLoggingAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.Result = new LoggingActionResult(new HttpUnauthorizedResult(), filterContext);
    }
}

public class LoggingActionResult : ActionResult
{
    private readonly ActionResult innerActionResult;
    private readonly AuthorizationContext filterContext;

    public LoggingActionResult(ActionResult innerActionResult, AuthorizationContext filterContext)
    {
        if (innerActionResult == null)
            throw new ArgumentNullException("innerActionResult");
        if (filterContext == null)
            throw new ArgumentNullException("filterContext");

        this.innerActionResult = innerActionResult;
        this.filterContext = filterContext;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        // Do logging (or apparently you want auditing) here
        Log(this.filterContext);

        innerActionResult.ExecuteResult(context);
    }
}

NOTE: I would name them AuthorizeWithAuditingAttribute and AuditingActionResult since you clearly want auditing, not logging in this case.

like image 149
NightOwl888 Avatar answered Nov 12 '22 02:11

NightOwl888


One route you could go with would be use StackFrame. This would be a little cleaner than what you have currently. More details can be found here: How can I find the method that called the current method

like image 1
Jacob Malliet Avatar answered Nov 12 '22 04:11

Jacob Malliet