In preparation for an official FubuMVC release I’m making an earnest effort to get some adequate documentation together. To get the ball rolling, I’m going to publish the drafts on my blog starting with a series of posts on integrating authorization rules with FubuMVC (because one of our Dovetail teams could really use some documentation on this topic ASAP).
In case you didn’t already know, FubuMVC is the “for us, by us” web development framework. As such, FubuMVC tends to collect features only when one of the teams using Fubu needs something new. Late last summer my team needed to ramp up our story for authorization throughout our applications and not coincidentally, FubuMVC suddenly got a whole bunch of new features around authorization.
This post is kind of a problem statement / table of contents. Hopefully, I’ll follow up quickly with some code-centric content on the specifics.
Enforcing authorization rules on Http requests
The first thing we needed was to lock down Http requests based on authorization rules. We try to prevent a user from even attempting an action for which they aren’t authorized, but that’s about user experience and never abrogates your responsibility to lock down access at the server itself – especially when you’re using “hackable” Url’s.
Our detailed requirements were to support:
- Support simple role based checks. It’s the most common use case by far, so it’s crucial to make this feature frictionless to use.
- Support arbitrary authorization policies. Role based permissions took us about 80% there, but every sizeable system I’ve ever built has needed some “special” authorization rules based on object state, company organization, or the alignment of the stars on a given night. With FubuMVC, we allow you to register authorization rule objects supporting a well known interface on any endpoint in the system.
- Combine multiple authorization rules on a single endpoint. In order to pull this off, we had to support “Allow” / “Deny” / “None” semantics in the authorization rules. For example, I can specify that accessing a certain view in our system requires the role “PageViewer,” but that same view may also have an additional rule that says “deny access to this page if the content is sensitive and the user does not have the ‘MostTrusted’ role.”
Enforce degrading access for individual fields
A common use case in Crud-intensive applications is to apply authorization and/or business logic rules at the field level in a screen to either degrade the access to a field from editable to “read only” or even to hide the field element altogether. As of right now, FubuMVC supports:
- Registering rules for field access based on a common interface called IFieldAccessRule
- Field access rules can specify degrading access in terms of “All” or “Read Only” or “None”
- Combine multiple rules on the same field. Field access rules can be either authorization rules like role based checks or business logic conditions like “disable these fields if the Issue is closed.”
- Integrate with the Html conventions support in FubuMVC to “degrade” screen elements from the editable version of a field to a read only version of a field. This is a crucial feature for us.
- If the rules for a field evaluate to “Read Only,” do not render the field element and any associated “chrome” content like labels or validation holders.
- Apply field level policies at the server. We use a lot of field by field “edit in place” elements in our application. We apply field level authorization checks on the Ajax POST to update the field in question.
A key takeaway of points 5 and 6 is the importance of using the Html conventions when building screens. Making the html conventions do the work of degrading access allowed us to keep the view markup very clean (no if/then statements cluttering things up) and gives us much more reversibility by allowing us to change the field access policies in the system without having to modify the view pages.
Note to self: now that we “bind” directly to domain entities in some circumstances, tie the field level access to model binding to prevent a user from updating a field with http spoofing.
Integrate the authorization rules with User Interface elements
Ideally, a user should never get a 403 response in your application because you either hide or disable all the UI elements that would trigger an action the user isn’t authorized to do. I’m a lazy programmer and the thought of running around and adding explicit “if/then” tag soup to our views wasn’t very attractive to me. Instead, we were able to tie authorization rules directly to the Html helpers we use for navigation and command elements in FubuMVC.
The Html helpers for links or the menu support in FubuMVC exercise the authorization rules for a resource/endpoint before rendering a link, command element, or a menu item. If a user isn’t authorized to perform that action or access the view represented by the link, the FubuMVC Html helper simply doesn’t render the element.
Authorization rules should be extensible
One way or another you have to associate authorization rules with actions, endpoints, and fields in your system. We started the authorization effort knowing that we needed to be able to apply additional authorization rules in customer extensions without modifying our core application code. We also started by trying to use conventions to determine authorization rules. To that end, FubuMVC allows you to apply authorization rules to endpoints or field by:
- Decorating fields or action methods with attributes.
- Using conventions to register authorization rules on BehaviorChain’s at application startup
- Explicitly add authorization rules through the FubuRegistry DSL
- Register additional authorization rules to core endpoints from “Packages” (Bottles). The obvious example of us has been using customer specific authorization rules against core pages. The key value for us is being able to add new behavior for a customer without having to modify the core code.
The way that FubuMVC integrates authorization with the view elements and the runtime pipeline is a bit “magical.” That’s great in terms of initial development effort, but it almost always comes at the price of making troubleshooting and debugging harder. To that concern, we’ve endeavored to extend diagnostics into the authorization subsystem. FubuMVC includes diagnostic views to see which authorization rules are applied to each endpoint and find gaps in authorization coverage. Depending on how conscientious convention writers are, FubuMVC can record an explanatory trail explaining how a convention or policy behaved at configuration time. If you are using the request tracing, FubuMVC records which authorization rules fired and their results during any given request.
I’ll start filling in some technical details about how this stuff works. I think that this post will end up being kind of a table of contents in the FubuMVC wiki. Until then, please feel free to give any feedback or ask questions.