views:

389

answers:

2

I'm using an ActionFilterAttribute to do custom authentication logic. The Attribute will only be used on a derived Controller class that contains my authentication logic.

Here's my Controller, derived from my custom controller class, and a sample attribute:

public class MyController : CustomControllerBase
{

   [CustomAuthorize(UserType = UserTypes.Admin)]
   public ActionResult DoSomethingSecure()
   {
      return View();
   }

}

Here's an example of my ActionFilterAttribute:

public class CustomAuthorizeAttribute : ActionFilterAttribute
{
   public MyUserTypes UserType { get; set; }

   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
      myUser user = ((CustomControllerBase)filterContext.Controller).User;

      if(!user.isAuthenticated)
      {
         filterContext.RequestContext.HttpContext.Response.StatusCode = 401;
      }
   }
}

Works great.

Here's the question: Can I demand that this attribute ONLY be used on Actions in my custom controller type?

+2  A: 

You can put the ActionFilter on the class itself. All actions in the class will realize the ActionFilter.

[CustomAuthorize]
public class AuthorizedControllerBase : CustomControllerBase
{
}

public class OpenAccessControllerBase : CustomControllerBase
{
}

public class MyRealController : AuthorizedControllerBase 
{
    // GET: /myrealcontroller/index
    public ActionResult Index()
    {
        return View();
    }
}
Jarrett Meyer
I'm just going the route of defining the ActionFilter within the class (so it is not available on other classes.) It's good to know that the attribute can be defined across ALL ActionResults in a controller.
Peter J
+1  A: 

Based on the comments and the constraints of my system, I took a hybrid approach. Basically, if the request comes through via a cached route or the "User" is not set for any reason, authentication fails in the proper way.

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
  private MyUser User { get; set; }

  public override void OnAuthorization(AuthorizationContext filterContext)
  {
    //Lazy loads the user in the controller.
    User = ((MyControllerBase)filterContext.Controller).User;

    base.OnAuthorization(filterContext);
  }

  protected override bool AuthorizeCore(HttpContextBase httpContext)
  {
    bool isAuthorized = false;
    string retLink = httpContext.Request.Url.AbsolutePath;

    if(User != null)
    {
      isAuthorized = User.IsValidated;
    }

    if (!isAuthorized)
    {
      //If the current request is coming in via an AJAX call,
      //simply return a basic 401 status code, otherwise, 
      //redirect to the login page.
      if (httpContext.Request.IsAjaxRequest())
      {
        httpContext.Response.StatusCode = 401;
      }
      else
      {
        httpContext.Response.Redirect("/login?retlink=" + retLink);
      }
    }

    return isAuthorized;
  }
}
Peter J
If you are redirecting in any "isAuthorized=false" case, then why don't you just return true at the end?
Alex
In the case of the IsAjaxRequest flow, the user is not redirected, so the method still needs to return.
Peter J