I recently finished a rather large, complex, ColdFusion-based (of course) application. The security within the application is more robust than anything that I've ever coded before; it works well enough, but it certainly has left me wanting to find a better solution for applications going forward. I've spent the meditating on security ideas and I just can't seem to find the sweet spot.
The application is event-based just like most ColdFusion applications that we see out there. As such, each request maps to a single event. For example, several news-related page requests might use one of the following events:
The security surrounding events in the application is both "event-based" and "target-based"; by that I mean that access to a given event is checked at both the event level and, if necessary, at the target level. Taking the news-related events as an example, all users in the system might have access the "news" event. But, only administrators can access the "news.edit" event in general; but, even within the "news.edit" event, administrators may only edit target items (ex. news record, ID=2403) that they themselves created.
Now, to complicate things even further, each user within the system can have several different roles associated to the same login. So for example, Molly might have a single set of credentials but belong to both the Administrators and Staff groups. Even though she belongs to multiple groups, each page request that Molly makes can only be evaluated in the context of a single mode (due to display constraints). This mode is based on the directory being accessed:
So what does this all mean? Permissions are assigned at the time of login and a user might have permissions for multiple groups; but, event access is challenged based both on user-specific permissions AND page-specific settings.
At first, I started playing around with the API that I would want to use and thought it would be elegant to see this all come off some sort of User object:
User.GetPermissions( "news" ).CanAccess()
User.GetPermissions( "news.edit" ).CanAccess()
User.GetPermissions( "news.edit" ).CanAccess( Target = 2403 )
Implementation aside, this seems nice - until you take into account the fact that access is challenged in the context of page-specific settings. Since a user might be making simultaneous requests in different modes (it's very common for a single user to have multiple browser tabs open in different "modes"), I can't store page-request-mode information inside the user.
Given this situation, I would either have to pass in the page-specific settings to the permissions method:
User.GetPermissions( RequestSettings, "news.edit" ).CanAccess()
... or, I can totally shift my mental model regarding security and start thinking about access as a request-based challenge rather than a user-based challenge. Meaning, security is no longer about what the User has access to, but rather about what the Request has access to:
Request.GetPermissions( "news.edit" ).CanAccess()
Using this methodology, we could easily instantiate a Request object using both page-settings and a User instance.
The request-based methodology leads to a cleaner API, but I think we can all agree that there's something about it that just doesn't feel right; security should be user-based because it's the user that requires security in the first place. So, how can we keep it user-based but still use a clean API? One way might be to simply pass in an entire Event object to the permissions method that aggregates both the page-specific settings and the event:
User.GetPermissions( EventObject ).CanAccess( Target = 2403 )
This feels OK, but falls apart when we realize that when it comes to security, we don't just check the current page request access - we check the current page request and conditions regarding future page requests. Meaning, of course we have to challenge the current request permissions, but with things like page layout, we also have to check out future page requests:
<cfif NOT XXXXX.GetPermissions( "news" ).CanAccess()> <!--- Throw Authorization error. ---> </cfif> <!--- Check Add rights. ---> <cfif XXXXX.GetPermissions( "news.edit" ).CanAccess()> <a href="##?event=news.edit">Add News Item</a> </cfif> <!--- Check edit rights. ---> <cfif XXXXX.GetPermissions( "news.edit" ).CanAccess( Target = 2403 )> <a href="##?event=news.edit&id=2403">Edit Item</a> </cfif>
Because of this, we need a way to not only challenge access for the current page request but also to check access for subsequent page request events. This either puts the kibosh on passing in an Event object that contains the page-specific settings and the event (as the event must be dynamic), or it means that we simply need two different methods for checking security. I think clearly, if we have two ways to perform the same action, that's a huge red flag; so, I think the only logical conclusion is that we have to dump the unified Event concept when checking security.
At this point, I only see what doesn't work, not what works. To be continued....
Want to use code from this post? Check out the license.