Creating a blacklist route constraint

Sometimes you want to take control over all of your requests prior to them getting too deep into your application. While you could do this with an HttpHandler or an HttpModule, that wouldn't be very MVC-like. Instead, we will implement this sort of logic in the next step of the pipeline by way of a custom RouteConstraint.

In our example, we will take a look at implementing a blacklist filter, using a wide open wildcard route and a RouteConstraint that checks all requests against a few different types of blacklists. If the user is on the blacklist, we will have the option to route them off our site or to a page in our site specifically for blacklisted folks.

How to do it...

  1. Create a new MVC application.
  2. Open up the Global.asax file. In the routing section, at the top of the list of routes, we need to create a new route. This route will be named BlacklistFilter and will have only a wildcard entry for its path pattern matching, essentially funneling all requests through this route for a check. Then comes the magic in that we will specify a custom RouteHandler called BlacklistConstraint.

    Global.asax:

    routes.MapRoute("BlacklistFilter", "{*path}", new { controller = "Blacklist", action = "Index", }, new {isBlacklisted = new BlacklistConstraint()}
    );
    
  3. Then we can create a new controller called BlacklistController in the Controllers folder. This controller will have one action on it named Index and will take in a parameter named reason which we will use to capture the reason the user is on the blacklist.

    Controllers/BlacklistController.cs:

    public class BlacklistController : Controller
    {
    public ActionResult Index(string reason)
    {
    ViewData["Reason"] = reason;
    return View();
    }
    }
    
  4. From this new action, we can generate a new Index view, which will be an empty view. In the view, we will display a simple message to the user telling them that they have been blacklisted. We will also say why we think they have been blacklisted.

    Views/Blacklist/Index.aspx:

    <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Blacklisted!
    </asp:Content>
    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Uh oh!</h2>
    <div>
    It looks like you have been blacklisted. If you feel that you are seeing this mistakenly please contact us at...
    </div>
    <fieldset>
    <legend>Why was I blacklisted?</legend>
    <%= ViewData["reason"] %>
    </fieldset>
    </asp:Content>
    
  5. Lastly, we will need to create our custom RouteConstraint. Create a new class in the Models folder named RouteConstraint. Set this new class to implement IRouteConstraint and then create the one required method, Match().

    Models/BlacklistConstraint.cs:

    public class BlacklistConstraint : IRouteConstraint
    {
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
    }
    }
    
  6. All that this method needs to do is return a Boolean result. Whatever method you use to determine if a user is on your blacklist is up to you. If you locate the user on the blacklist, return true, which will route them to your blacklisted page. If you don't find them, return false, and the processing will continue. You may also want to drop a cookie or set a session variable so that the check is not repeated over and over—unless you want to. For our recipe, we will define some blacklists directly in the class, for example purposes only. We will also create a simple variable to track if this user is flagged or not.

    Models/BlacklistConstraint.cs:

    bool blacklisted = false;
    //you could get your blacklist data from a database
    //and cache it when the app starts
    //ip address list
    List<string> _ipBlacklist = new List<string>()
    {
    "127.0.0.1",
    "192.168.1.100",
    "192.168.1.101",
    "192.168.1.102",
    "192.168.1.103",
    "192.168.1.104",
    "192.168.1.105",
    "192.168.1.106",
    "192.168.1.107",
    "192.168.1.108",
    "192.168.1.109",
    "192.168.1.110"
    };
    //username list
    List<string> _usernameBlacklist = new List<string>()
    {
    "asiemer",
    "anonymous"
    };
    //email list
    List<string> _emailBlacklist = new List<string>()
    {
    "[email protected]",
    "[email protected]"
    };
    
  7. Once you have the mechanism in place to keep track of who is blacklisted, you can add the actual checking implementation. For us, this will be a few checks to see if the user exists on any of our lists. If we find that the user is on the list, then we will set our flag to true, signifying that they match the constraint and can be processed by this route. We will also set a reason variable in our route data that can be displayed in our blacklist index view.

    Models/BlacklistConstraint.cs:

    //check ip addresses
    if (_ipBlacklist.Contains(httpContext.Request.UserHostAddress))
    {
    values.Add("Reason", "You were blacklisted because of your IP address: " + httpContext.Request.UserHostAddress);
    blacklisted = true;
    }
    //check usernames
    if (httpContext.Profile != null && _usernameBlacklist.Contains( httpContext.Profile.UserName.ToLower()))
    {
    values.Add("Reason", "You were blacklisted because of your username: " + httpContext.Profile.UserName.ToLower());
    blacklisted = true;
    }
    //check email addresses
    if (_emailBlacklist.Contains("values['email'].ToString()"))
    {
    values.Add("Reason", "You were blacklisted because of your email address: values['email'].ToString()");
    blacklisted = true;
    }
    
  8. Once we have processed all of our lists, we can then handle the fact that the user is indeed blacklisted. In our case, we could redirect the user to PeopleOfWalmart.com or we could send them to our blacklist page, where they can see that they are blacklisted as well as the reason for which they are blacklisted. We will also set the HttpStatusCode of our response to access denied (403).

    Note

    Be aware that telling your blacklisted user that they are actually blacklisted, as well as why they are blacklisted, may not be the best way to handle such a user. If it is to be a temporary blacklist due to abuse of a site feature, telling the user might be appropriate. Telling a malicious user that they are blacklisted due to their IP address might get them to go through a proxy server prior to coming to your site. Just think about what you do here.

    Models/BlacklistConstraint.cs:

    //do some stuff before booting the user
    if (blacklisted)
    {
    //of, if you don't want to keep them around...
    //httpContext.Response
    .Redirect("http://www.peopleofwalmart.com");
    //set the status code to access denied
    httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
    }
    //return the result
    return blacklisted;
    
  9. From here, you should be able to run your application. The sheer act of attempting to view the home page from your local computer should get you blacklisted from your site, as most likely you are browsing from 127.0.0.1. If for some reason you are not blacklisted by IP address, you can run ipconfig at the command prompt to get your current IP. Or you can create an account in the site and add your username to the username list.

How it works...

This recipe takes advantage of an extension point of the MVC framework. Specifically, we used a custom RouteConstraint to add to our route definition. The nice bit about the RouteConstraint is that anything goes here, as it is just a standard class from which we can do pretty much anything. The combination of the route definition and this added bit of Boolean logic makes your routing capabilities very powerful.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset