Part 2: Annotated Timok Project, or "How to secure your home"
Oy vay, the hillbilly has to be more careful adding Part 1 to the beginning of a post. Too many implications about his commitment involved.
This is a follow up to a post I did to help guide new MonoRailers through the Timok sample application. If you're just joining us, or have hit me from a random search engine (oh, let's just say it: Google), the intent is to help get up to speed with MonoRail by explaining the steps one might go through when looking through a sample application. I haven't been diving too deeply into the topics because I'm not really a deep-diver by nature, unless it is required. Scared I might get the coding equivalent of the bends.
A friendly reminder/disclosure/EULA that, for the most part, I'll avoid any "I think"s and "It looks like"s and "My guess is"es for the sake of readability. Consider this your unqualified qualification that anything I say here is the result of my own investigations with a liberal helping of my previous web experience.
When we left off, we had gone through how MonoRail routes requests to an appropriate controller and how that controller determines which HTML to render on the page. We walked through a request to /home/index.rails which led us to the default.vm layout in the Views/layouts folder and the index.vm view in the Views/Home folder in Timok.Rbr.Portal.Web. index.vm contains the HTML that will be rendered within the default.vm layout (akin to a "master page"). But before we div---er, wade into index.vm, we need to back up a bit.
I tried to navigate to /home/index.rails but was redirected to a login page. WHAT GIVES?!?
Open up HomeController.cs again and notice that it derives from SecureController, which in turn derives from ControllerBase, which in turn derives from SmartDispatcherController. SmartDispatcherController is the main MonoRail controller class and all your controllers must derive from it (or its own base class, Castle.MonoRail.Framework.Controller, but that isn't as common).
It's common for MonoRail applications to create their own ControllerBase derivation, similar to creating your own PageBase in traditional web forms. In this case, we also further derive a SecureController class which, as its name implies, allows you to secure access to any controllers that derive from it.
How does SecureController restrict access to controllers?
The class has the following attributes:
[Filter(ExecuteEnum.BeforeAction, typeof(AuthenticationFilter), ExecutionOrder=1)]
This tells MonoRail that before any action can be performed on this controller, we need to first process a filter of type AuthenticationFilter. ExecutionOrder is helpful when more than one filter is applied to a controller, which isn't the case here.
Note that the controllers.config file contains an entry for our AuthenticationFilter class. While the above attribute indicates that we have to use a class of type AuthenticationFilter for the filter, it doesn't say which class specifically needs to process the request. This is another example of how MonoRail uses Windsor, the IoC container. By registering AuthenticationFilter in the controllers.config file, we tell MonoRail "if you need a class of type AuthenticationFilter somewhere and one hasn't been specifically requested, use this one".
OK, fine, so how does *AuthenticationFilter* restrict access to controllers then?
I'm not sure I like your tone, kiddo. Maybe I should just leave you to figure this out on your own, yesno?
No, no, no. I didn't mean it like that. I apologize. Please continue.
That's better. AuthenticationFilter (which derives from MonoRail's Filter class) overrides OnBeforeAction. This code will be executed before any action is performed on a controller that uses this filter. And there isn't anything particularly MonoRail-ish in this code. It checks to see if users are logged in using plain old System.Web.Security.FormsAuthentication. If they aren't, they are redirected to the login page. Or rather, they are redirected to the Index action of the Login controller.
If they are authenticated, OnBeforeAction returns true, which means the request will filter through to the originally requested action (in our case, the Index action on the Home controller). The last line of OnBeforeAction says "return false". The code never makes it this far because it either returns true or redirects to the login page before it gets here. But if it did get to this line and returned false, this tells the controller using this filter that it didn't "pass the test". The controller will then display a blank page which you can verify by commenting out both SendToLoginPage lines.
Summary
Two posts and we still haven't actually rendered anything yet. Yeah, well, security has a tendency of grinding everything to a halt. Here we discussed:
- Applying filters to controllers to execute code before an action is processed
- Deriving your own controller hierarchy
- Using Windsor again to provide a class when one isn't specifically requested
- Being polite to the hillbilly
Next up: We'll finally look at some rendered code and, with luck, what happens when you do something on the page.
Kyle the Insecure