Search code examples
c#asp.net-mvcactionmethod

ASP.NET MVC - Optionally Redirect from a Base Class Method?


Consider the following block of code that reappears in many of my controller actions. (I'm chiefly concerned with the first 6 lines of the method body).

[HttpGet]
public ActionResult OptOut()
{
    var user = this.SecurityPrincipal;
    if (user.IsReadOnlyUser)
    {
        this.TempData["ViewModel"] = new AuthorizationModel { User = user };
        return this.RedirectToAction("NotAuthorized", "Authorization");
    }

    var model = /* Elided for brevity */

    return this.View(model);
}

My controllers derive from a base class, SecuredController which, in turn, derives from Controller. SecurityPrincipal is a property of SecuredController, and contains extensive Active Directory data about the current user.

In an effort to eliminate duplicate code, I'd ideally like to move the functionality contained in the if {...} block into a base class method, but I can't think of any way to do so, since the return type of the method would have to be ActionResult, resulting in something ungainly like this:

if ((var result = this.RequireReadWrite()) != null)
{
    return result;
}

Can anyone suggest a way to do this, or am I simply out of luck here?


Solution

  • As mentioned in the comments, especially noting that security is a cross cutting concern we've suggested using MVC Action Filters to be applied in your use case and design.
    Microsoft's documentation is pretty informative and there are more examples that can be found on the web on how to use MVC Filters. I'll try to provide an example, but this will be based on a lot of assumptions on your software architecture, since I simply don't have the knowledge of that.

    You could create the following class:

    public class SecuredFilterAttribute : AuthorizeAttribute
    {
        ...
    }
    

    If using a Dependency Injection framework, you could inject the SecurityPrincipal service. But again I don't know the architecture of your application, so it's up to you how you create that dependency.
    When overriding the AuthorizeCore, you could implement it like so:

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        return !this.SecurityPrinciple.IsReadOnlyUser;
    }
    

    And when not authorized override the HandleUnauthorizedRequest method to redirect:

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        var redirectRoute = ...; //your route to redirect to an unauthorized page
        filterContext.Result = new RedirectToRouteResult(redirectRoute);
        //do some other things, for example, setting some tempdata information
    }
    

    Again it's up to you on how you would use this Filter. You could register it globally, or apply it on a per controller or action basis. To register it globally, in your startup:

    GlobalFilters.Filters.Add(new SecuredFilterAttribute());