One of the most important tasks for any dynamic application is providing the mechanisms to secure it and manage its users. In the past Cold Fusion developers have had very little guidance in creating these mechanisms. With the advent of MX technology from Macromedia we now have a set of common tools that can be applied and utilized not only in Cold Fusion, but also in richer Flash interfaces. This series of articles will explore the task of securing an application and managing users through a roles based component driven model.
Most security models handle three different functions ? Membership, Authentication and Authorization. A Member of an application should have the ability to join and edit their own information. Authentication can be resolved simply by answering the question ?Is this user a member of this application?? Authorization can be a bit more complex. Is the user authorized to see this Application? If they are authorized to see the Application are they allowed to view a particular file? If they can see the file which pieces of the file are they authorized to see? If they can see a piece of a file what data are they authorized to see?
Macromedia's MX technology gives developers the ability to handle complex tasks by breaking them up into self-contained Objects. The security model we will be building will have three main objects: member, role, and security. The goal in designing these objects is to make them generic enough to be reused in multiple applications and from multiple technologies. We will begin with a generic html interface to these objects and later on we will add a flash interface to the same objects.
Initializing the Sample Code
The sample code provided with this documentation is a fully functioning application that provides bothe the components necessary to administer a site and it?s users. This includes adding and editing both users and roles. All passwords are stored as hashed values for an additional layer of security.
The components handle all of the business logic necessary to run the application. I have provided Cold Fusion based views to allow you to interact with the application. These views can just as easily be provided by Flash or any other visual interface.
In order to initialize the code accompanied with this example the first user needs to be assigned to the Administrator role. This is accomplished with a block of code located in join.cfm on line 15. If you are going to be using this example it would make sense to delete that block of code after you sign yourself up as a user.
This article does not cover invoking components and relies on the self documenting features of Cold Fusion Components. All of the components are located in /com and you are encouraged to view them in a web browser to see the documentation.
The Big Picture? DB Structure
Our completed project is going to utilize the tables outlined above. The member table will store information about each member. Each member will have one or more related entries in the security table. The security table will store the member?s username and a hashed password. The roles table holds each of the roles for our application. The securityLink table links the security table to the roles and the security table.
Establishing the Framework
The cfsetting tag suppresses all output unless generated by Cold Fusion.
<cfsetting enablecfoutputonly="yes" />
In this example we need to initiate session and client management. We are going to set a cookie that stores the CFID and CFTOKEN so that we have the ability to track the user as they move through the application. We are going to use a data source entitled clientVariables to store client information. By using a database for client variable we remove any critical security information from the client machine.
Application Wide Variables
In order to make the application as dynamic as possible we need to set a few application variables. Whenever declaring Application, Server, or Session variables it is important to lock the scope. We need to define a data source name, the web root of the application, and the root of the components our application will be using. Because these variables are applicable to all users we are going to place them in the Application scope. The settings below tell us the application uses the data source ?security?, can be reached form the url http://localhost/security/ and that our components are locate at http://localhost/security/com/
<cflock scope="Application" timeout="10" type="Exclusive" />
<cfparam name="Application.dsn" type="string" default="security" />
<cfparam name="Application.webRoot" type="string" default="/security/" />
<cfparam name="Application.cfcRoot" type="string" default="security.com" />
In our application authenticated users will have the ability to log out. The logout link will simply redirect the user to the current page they are looking at, but it will append the url variable logout=1. If this variable is defined we don?t want to process anything else before logging out the user. It is important that we place the We won?t need to redirect the user because our security system will be able to handle an un-authenticated user from any location. The cflogout tag removes knowledge of the user and users roles from the server. If the cflogout tag does not get called the user is automatically logged out when their session expires.
<cfif isDefined("url.logout") AND url.logout>
The cflogin tag and the processing it contains will only execute if we do not have a logged in user. We need to check for the existence of a cflogin scope which will get generated by Cold Fusion when a form named cflogin is submitted . The cflogin scope will always contain the variables cflogin.name and cflogin.password. If the cflogin scope is in existence we create a Security object that contains all the methods of the security component by using the createObject function to invoke security.cfc.
In order to authenticate the user we are going to call the authenticate method our Security object and pass it cflogin.name and cflogin.password. The authenticate method will either return a list of roles if the user passes authentication, or a 0 if the user fails.
If the authenticate method has returned a list of roles we use the cfloginuser tag to log the user in and set the users roles.
Security = createObject("component", "#Application.cfcRoot#.security");
<cfif variables.roles NEQ 0>
<cfloginuser name="#cflogin.name#" password="#cflogin.password#" roles="#variables.roles#" />
The final line of the Application.cfm simply includes a header file. Our header is going to declare any UDFs we might need throughout the site and display a menu that is generated based on the users authentication state and roles. Please see that section for more detail.
<cfinclude template="#Application.webroot#_dsp/dsp_header.cfm" />
OnRequestEnd.cfm is another one of those ?special? cold fusion file names similar to Application.cfm. Unlike Application.cfm it get?s called at the end of each http request. It is an ideal location to place application wide error handling capabilities, but for the purposes of our example it only calls a footer file that we will use to close our html documents.
<cfinclude template="#Application.webroot#_dsp/dsp_footer.cfm" />
The cflogin scope is. It is required that you use the fieldname j_username and j_password if using a form. The j_username gets mapped to cflogin.name and j_password gets mapped to cflogin.password.
The login page will only get called if the person browsing the site tries to access an area they do not have rights to view. The processing that determines if the user can access a page happens before any content gets displayed. Therefore if the login page gets displayed we want to stop all other processing on the page so that any secured content does not get displayed. This is accomplished simply by placing a <cfabort> at the end of the login page. This effectively stops the user from viewing the content, displays a login page and a message, and is all accomplished without redirects and additional stress on the server.
The Security Checks
In this demonstration we are going to focus on two distinct types of security checks: Page Security, and Data Security.
Page security applies to the entire page of content and all the items that may be contained in that page. An example of this type of security can be found in the document: /members/index.cfm.
Cold Fusion MX has provided a new function getAuthUser() which will return the name of an authenticated user. We begin by checking the length of getAuthUser(). If the length of getAuthUser()is zero we know that the user has not logged in and needs to view the login page. Otherwise we simply let them pass and do nothing ? pretty simple!
<cfif not len(getAuthUser())>
<cfset request.loginmessage = "You need to login." />
<cfinclude template="#Application.webroot#security/login.cfm" />
Data Level Security applies in instances where multiple users can view data but that data may change based on the users roles. An example of this type of security can be found in /_dsp/dsp_header.cfm and controls the menu.
There are three different states that we can check a user for: unauthenticated, authenticated, and member of a role. The first state (unathenticated) is simple and the one we are all used to. You simply output whatever data you want the unathenticated user to see:
We check for an authenticated user as outlined in the Page Security section. By Evaluating the length of the function getAuthUser() we can easily determine if the user has logged into our application. Using this method we ensure that our data is protected from all unauthenticated users.
<a href="#cgi.script_name#?logout=1">Logout: #GetAuthUser()#</a>
The final check is to see if a user is a member of a particular role that we have defined. Cold Fusion MX provides us with the function isUserInRole(?role_name?) which returns true or false.
<a href="#Application.webroot#member/admin.cfm">Administer Members</a>
By utilizing the security functions provided by Cold Fusion MX we can easily secure any application in a flexible manner.