There's no denying it: the plethora of documentation and examples that exist for the Coldbox framework is invaluable and undoubtedly covers every possible scenario to one degree or another. As I press on into a project where the use of Coldbox, Coldspring, and Transfer has been mandated to me, I'm finding that, despite the documentation and tutorials, I'm still having to search around and piece together for myself some of the information I seek. So, to complement the existing volumes of Coldbox-centric information and for my own personal reference purposes, I'm going to post some of what I'm learning as it becomes coherent to me. Tonight's post is focused on an overview of how to implement basic security in Coldbox using Transfer and Coldspring. I won't delve much into Transfer or Coldspring with this one (that post will follow soon) so that we can focus on the 10,000 foot view first.
As I mentioned, the first "block of functionality" I tackled was the implementation of security, so let's use that as our illustration. There is a lot of documentation on the subject and even a few example apps I found. But, the documentation left me with too much gray area as I attempted to relate what I read with my current level of understanding of the framework, and the example apps, though they worked wonderfully, didn't explain to me HOW they were working. I had to dissect the apps using my limited knowledge of Coldbox and painstakingly trace the event flow from one area of the framework and application to the next, referring to the documentation and even examining some of the framework's CFCs internally. The end result for me was the foundation for my own "flavor" of basic security implementation, and it this that I share.
First, my own visualization of the Coldbox event lifecycle. I realize that the true lifecycle in all its detail could consume many a diagram page, but for the purposes of someone simply desiring to add in a bit of functionality, I do believe I've illustrated all that is required.

A request comes in and right away Coldbox executes the interceptors it knows about. Interceptors are very aptly named, as they do just that: intercept the request before it gets too far and executes whatever methods are appropriate. In our case, we're interested in knowing right up front if the user attempting to fulfill an http request is authenticated or not. Intercept them, then, we shall.
Coldbox comes out of the box with a pre-built security interceptor (a CFC that is executed at what we typically know as the "onRequestStart" point), but being at the newbie point I am, I found it a bit too deep to assimilate. In order to understand how things really work, therefore, I created my own. I called it "security.cfc" and dropped it into my app's "interceptor" directory. The next step was to tell Coldbox that I have an interceptor in place. This is done in config/coldbox.xml.cfm, in the <Interceptors> section. Mine looks like this: I eluded to it, but now is a good time to talk about one of the things that makes Coldbox unique among the MVC frameworks we CFers have to choose from. If you've not used any framework before that relies on what's typically referred to as "convention" (Ruby on Rails, Fusebox 5.x sans xml), then you are used to defining your events down to every last detail: what messages to broadcast, what methods to execute in what CFC, etc. But Coldbox rather follows the "convention" approach, which is simply naming your CFCs and methods in such a way that the framework knows what to do all by its lonesome. This really isn't a new thing to you at all though, because if you've used Application.cfc in any of your apps, then you have practiced this very thing! Consider this...did you ever have to TELL your app to run application.cfc? Nope, because of the fact that you NAMED it application.cfc, the framework (in this case, CF Server) looked for it and executed it. Inside of your application.cfc you had some methods, right? You had one named "onApplicationStart", one named "onRequestStart", one named "onSessionStart", etc. Why? Because you knew that if you named your method appropriately, the "framework" would execute it at just the right time in the http request lifecycle. This is absolutely no different in Coldbox. Inside our interceptors we create methods that are named according to when we want Coldbox to execute them. Granted, Coldbox uses a few names of its own to indicate a specific point in the event lifecycle (such as 'PreProcess'), but they boil down to the same concept as what we're accustomed to with Application.cfc. So, I told Coldbox "hey, I have an interceptor I want you to execute for me at the beginning of the event lifecycle", then within that interceptor I created methods to run at specific points in time. In the case of my Security.cfc interceptor, I have create a single method called "preProcess" (guess when it's going to be executed). Within this method I am checking to see if the current user is logged in. If yes, do nothing; if no, redirect to my login event. In my simple scenario, "logged in" means i found session.loggedin and it was set to 'true'. Here's my interceptor method:
<Interceptor class="interceptors.security" />
</Interceptors>
<cfargument name="event" required="true" type="coldbox.system.beans.requestContext" />
<cfset var loggedin = false />
<cfset oSession = getPlugin("sessionstorage") />
<cfif (NOT oSession.exists("loggedIn") OR NOT oSession.getVar("loggedIn")) AND listfind("authentication.doLogin",arguments.Event.getCurrentEvent()) eq 0>
<cfset arguments.Event.setValue("loginmessage","Please Log In") />
<cfset arguments.Event.overrideEvent("authentication.login") />
<cfelse>
<cfset arguments.Event.setValue("message","we're logged in!") />
</cfif>
</cffunction>
Let's assume the user is NOT logged in. My interceptor resets the current event to my login event, "authentication.login", and processes it. POP QUIZ: Based on the event name "authentication.login", what CFC and method within that CFC do you suppose Coldbox will be executing? I'm sure you guessed right, so let's go look at the 'Login' method within my 'authentication.cfc'.
Hmmm, where IS my authentication.cfc? It's not an interceptor...where is it? Ah, it's where Coldbox ASSUMES it will be (there's that "convention" thing again!): sitting in the "handlers" directory. We Model-Gluers and other MVC framework people might think of it more commonly as "controllers", but the Coldbox vernacular is "handlers", as in "event handlers". Here is the Login method:
<cfargument name="Event" type="any">
<cfset arguments.event.setvalue("loginmessage","You need to log in!") />
<cfset arguments.event.setValue("loggedin",false) />
<cfset arguments.event.setvalue("xe.frmAction","authentication.doLogin") />
<cfset arguments.event.setView("footer") />
<cfset arguments.event.setView("login") />
</cffunction>
Being the good controller/handler that it is, it's doing next to nothing except setting a few values in the event bucket and tossing some views into the view collection so that we can properly give the user a login form.
Now we see the login form, nothing fancy, just a prompt for a username and password. The user enters it, hits the submit button, and off we go to our form's action: authentication.doLogin . Ignore the details for a moment, and let it suffice to say that we are grabbing the credentials out of the event bucket, submitting them to our model for authentication, and getting back a boolean value indicating success or failure. Again, our controller/handler method is doing next to nothing (as it should), except setting some values for the view to use.
<cfargument name="Event" type="any" />
<cfset var loggedin = "" />
<cfset var rc = arguments.Event.getCollection() />
<!--- attempt to authenticate using the credentials supplied... --->
<cfset loggedin = variables.authenticationService.login(username=rc.username,password=rc.password) />
<cfif not loggedin><!--- login failed...send them back --->
<cfset arguments.event.setValue("loginmessage","Login Failed. Please try again.") />
<cfset arguments.event.setValue("loggedin",false) />
<cfset arguments.event.overrideEvent("login") />
<cfelse>
<cfset arguments.event.setValue("loggedin",true) />
</cfif>
<cfset arguments.event.setvalue("xe.frmAction","authentication.doLogout") />
<cfset arguments.event.setView("login") />
</cffunction>
Wanna see the "login" method in the model? We're passing it two parameters and setting a third by default, then passing those parameters to Transfer to see if it can successfully populate a User bean with them. If yes, store the user bean to session so we can utilize it other places and return a boolean 'true'; if not, store the empty user object to session (in case we still want to use it somewhere else...at least we know it will exist) and return a boolean false.
<cfargument name="username" type="string" required="yes" />
<cfargument name="password" type="string" required="yes" />
<cfargument name="isActive" type="boolean" required="yes" default="true" />
<!--- create a transfer user bean, passing in the username and password, then give it back --->
<cfset var objUser = variables._transfer.readByPropertyMap("user.user",arguments) />
<cfset retval = false />
<cfif objUser.getID() IS "0"><!--- login failed... --->
<cfset variables._session.setVar("loggedin",false) />
<cfelse>
<cfset variables._session.setVar("loggedin",true) />
<cfset variables._session.setVar("user",objUser) />
<cfset retval = true />
</cfif>
<cfreturn retval />
</cffunction>
That's it in a nutshell! If I were me looking at this post two days ago, however, I would be very hungry to know the details of how I set up Coldspring and Transfer to play together, and how it is I am accessing my session scope and Transfer from within my model. In order to encapsulate the purpose of this post (give an overview of basic application flow for the purpose of implementing security), however, I shall end it here. But my next post will be the nitty gritty of how I wired all these pieces together.
Hope this helps someone, and do feel free to contribute your own wisdom to the comments!
You are not logged in, so your subscription status for this entry is unknown. You can login or register here.
There is also no denying that a coherent, step by step such as you're constructing here is invaluable for those who can't instantly grok by viewing an API listing and a hodge-podge of partial examples written for a number of versions and set-ups! (a slight note of frustration? yeah, you could say that!)
Louis has put together a fantastic framework... I believe. Being able to use it though, is a serious learning curve and thank you for adding to the material one can use to help the climb!

