Introduction By using Warden, you will avoid hard-coding user privileges in your application code. Instead, you will challenge specific actions, and if the security model changes for a piece of functionality, you simply update an XML configuration file and your code remains unchanged. It even supports multiple view states because actions can change their authorization requirements based upon the state of the application, not just user privileges. Warden is simple, the zip file contains the Warden class, a logging utility (which you can replace, or even remove) and a sample definition file. Create a warden directory in the root of your application server and extract the files there. Whenever certain aspects of your application need to be restricted to certain users, the basic way of controlling access is to put conditional logic around the feature. Here’s an example. Pretty simple, but now imagine that you have to lock down 200 features in your app with that condition. Ok, fine once it’s done it’s done and you never have to worry about it again… just a lot of copying and pasting. But wait, that was fine 6 months ago, but now your company has hired a couple of field trainers to go out and teach your customers how to use your product. In order for them to be able to effectively show off all of the features, they have to have access to much of the code your locked down to be for administrators only. Now you have to go through the process of copying and pasting a line of code 150 times again, while the remaining 50 features are still locked down to just administrators. Um, no. Next week, your company is hiring a product manager that needs access to 25 of those remaining 50 features that were admin only…. And you know what? the privilege APP_ADMINISTRATOR is too generic now because of the new roles. Can you go ahead and replace that string with APP_OWNER? See how this can quickly become a nightmare? Since you’ll want to use Warden throughout your application, a simplistic way to start would to be to create an instance of it in the Application scope. If you are using ColdSpring, you could easily create a Warden bean and inject it directly into other components, but there are risks to consider which I will cover later. Now, when you have a feature in your application that needs to be restricted to certain users, you simply need to challenge the action and pass the current user’s list of privileges. For now, Warden only accepts a comma-delimited list. I will support other types in the future, but you can also modify the code yourself quite easily. Then you create a Warden definition file with the following guidelines. The root element is challenges and must contain an application attribute. By default Warden will log debug messages to a file with this name. The only child element of challenges is challenge. Each action, or feature, that needs to be restricted in your application will have its own challenge, and will be identified by the id attribute. The only child element of challenge is allowedPrivileges. In most cases, there will only be one allowedPrivileges child in each challenge – the default set – which is identified by specifying the viewstate=”default” attribute. Lastly, the only child element of allowedPrivileges is privilege. Contained in each privilege node is a string containing each privilege that is allowed to access the challenged feature. Now, any time the access needs to change for a challenged feature, you simply update the privileges in your Warden definition file. I implemented the concept of view states because, very often, views in your application may have more than one state, or mode. For example, you may have a page that lists products with filtering options on the page so that, depending on what the user has chosen, the same view could show different products, or show different options. Let’s look at a basic example. Here’s the requirements for who can access the three links. Only administrators and trainers can switch to advanced mode. Also, when in advanced mode, the link should not appear for anyone (obviously). When in advanced mode, administrators can delete an album, and modify an album, so add a challenge for each action in the advanced view state with only administrator privileges. Also, when in advanced mode, the trainer should be able to modify the album, but not delete it. All you need to do is update the ModifyAlbum challenge to include the trainer privilege. This concept is simple. A user either must have one of the required privileges, or they must have all of the required privileges. In the former case (which is default) you would pass a parameter value of EXCLUSIVE, and in the latter, the value would be INCLUSIVE. So using the example from above, just assume that there is a feature on your site that requires a user to be both an administrator and a trainer in order to access it. Let’s say modifying an album requires both. All you do is make it an inclusive challenge.
Quick Links
Installing Warden
Using Privileges Without Warden
Managing Privileges With Warden
View States
Inclusive/Exclusive Privilege ChallengeIntroduction
Installing Warden
Using Privileges Without Warden
<h2>Application Settings</h2>
<ul>
<li>
Theme : #theme#
<cfif listFind(user.privileges, "APP_ADMINISTRATOR")>
< a href="/Modify/?id=#setting_id#">Modify Theme< /a>
</cfif>
</li>
</cfif>
</ul><h2>Application Settings</h2>
<ul>
<li>
Theme : #theme#
<cfif listFind(user.privileges, "APP_ADMINISTRATOR") or
listFind(user.privileges, "TRAINER")>
< a href="/Modify/?id=#setting_id#">Modify Theme< /a>
</cfif>
</li>
</cfif>
</ul><h2>Application Settings</h2>
<ul>
<li>
Theme : #theme#
<cfif listFind(user.privileges, "APP_ADMINISTRATOR") or
listFind(user.privileges, "TRAINER") or
listFind(user.privileges, "APP_MANAGER")>
< a href="/Modify/?id=#setting_id#">Modify Theme< /a>
</cfif>
</li>
</cfif>
</ul>Managing Privileges With Warden
<cffunction name="onApplicationStart" output="false">
<cfscript>
// Create an instance of Warden in the application scope
application.Warden = createObject("component", "warden.Warden").init();
application.Warden.loadChallengesFromXmlFile("/Warden/Warden.xml");
</cfscript>
</cffunction>
<cffunction name="onSessionStart" output="false">
// For this simplistic example, we will create a basic
// structure to hold the privileges
<cfset session.user = {} />
<cfset session.user.privileges = "TRAINER,APP_OWNER" />
</cffunction><h2>Application Settings</h2>
<ul>
<li>
Theme : #theme#
<cfif application.Warden.challenge("ModifyTheme", session.user.privileges)>
< a href="/Modify/?id=#setting_id#">Modify Theme< /a>
</cfif>
</li>
</cfif>
</ul><challenges application="CompanyApp">
<challenge id="ModifyTheme">
<allowedPrivileges viewstate="default">
<privilege>APP_OWNER</privilege>
<privilege>TRAINER</privilege>
<privilege>APP_MANAGER</privilege>
</allowedPrivileges>
</challenge>
</challenges>View States
<cfparam mode="#url.mode#" default="basic" />
<h2>Jazz Albums</h2>
<ul>
<cfloop query="#albums#"><cfoutput>
<li>Album name : #name#</li>
<li>Artist: #artists#</li>
<li>Release date: #release_date#</li>
<li>
<cfif application.Warden.challenge("DeleteAlbum", session.user.privileges, "advanced")>
< a href="/DeleteAlbum/?id=#album_id#">Delete Album< /a>
</cfif>
<cfif application.Warden.challenge("ModifyAlbum", session.user.privileges, "advanced")>
< a href="/ModifyAlbum/?id=#album_id#">Modify Album Properties< /a>
</cfif>
<cfif application.Warden.challenge("EnableAdvanced", session.user.privileges, "basic")>
< a href="/Browse/?mode=advanced">Switch to advanced view< /a>
</cfif>
</li>
</cfoutput></cfloop>
</cfif>
</ul><challenges application="CompanyApp">
<challenge id="EnableAdvanced">
<allowedPrivileges viewstate="basic">
<privilege>ADMINISTRATOR</privilege>
<privilege>TRAINER</privilege>
</allowedPrivileges>
</challenge>
</challenges> <challenge id="ModifyAlbum">
<allowedPrivileges viewstate="advanced">
<privilege>ADMINISTRATOR</privilege>
</allowedPrivileges>
</challenge>
<challenge id="DeleteAlbum">
<allowedPrivileges viewstate="advanced">
<privilege>ADMINISTRATOR</privilege>
</allowedPrivileges>
</challenge> <challenge id="ModifyAlbum">
<allowedPrivileges viewstate="advanced">
<privilege>ADMINISTRATOR</privilege>
<privilege>TRAINER</privilege>
</allowedPrivileges>
</challenge>Inclusive and Exclusive Challenges
<cfif application.Warden.challenge("ModifyAlbum",
session.user.privileges,
"advanced",
"INCLUSIVE")>
< a href="/ModifyAlbum/?id=#album_id#">Modify Album Properties< /a>
</cfif>