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")
.
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
andAuditingActionResult
since you clearly want auditing, not logging in this case.
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
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