Contact Doug!
Learn About Doug!
View Doug Boude's online resume
updated 11/18/2009

View Doug Boude's profile on LinkedIn
Link to me!

Follow Doug Boude on Twitter
Follow me!

Be Doug's friend on Facebook
Befriend me!
(I promise not to follow you home)
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...
Florida web site design



Czech your Page Rank!
Check Page Rank of any web site pages instantly:
This free page rank checking tool is powered by Page Rank Checker service
Surf's Up!
Visit Egosurf.org and massage YOUR web ego!
My Score: 9,001
Doug's Books

Read (and recommend)

  • Men are from Mars, Women are from Venus
  • The Wisdom of Crowds: Why the Many Are Smarter Than the Few and How Collective Wisdom Shapes Business, Economies, Societies and Nations
  • Blink: The Power of Thinking Without Thinking
  • Head First Design Patterns
  • Transact-SQL Programming
  • What's So Amazing About Grace?
  • Just So Stories (Rudyard Kipling collection)

Reading

  • Prayer: Does it Make Any Difference?
  • Data Mining (Practical Machine Learning Tools and Techniques)
<< June, 2007 >>
SMTWTFS
12
3456789
10111213141516
17181920212223
24252627282930
Search Blog

Recent Comments
Categories
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

25 September 2007
My Approach to Basic Security in a Model-Glue Application
By now almost all of us have "rolled our own" when it comes to giving a Coldfusion app some semblance of security, so the concept is nothing new. It can be, however, somewhat daunting when attempting to take what we know procedurally and apply it to an MVC styled application.

The Kansas City Coldfusion User Group invited me to assist them with a group project using Model-Glue, specifically asking me to show them how to implement security. What follows is my way of thinking about and approaching the implementation of basic security in a Model-Glue application. At the end of this post is a link to download the tutorial app I put together for them as well since I know "a line of code replaces a thousand lines of abstract explanations".

GOALS OF SECURITY

What we’re wanting to accomplish is this:
  1. User arrives at our app.
  2. We evaluate their current login status, setting the appropriate values in the appropriate places so that those portions of our app who DO care if the user is logged in or not can make the right choices.
  3. Those values follow the user along their tour of our app.
     

PRE-REQUEST ACTIONS

Putting this scenario under the MG microscope then, and following along the path of the MG event lifecycle, here’s what I want to make happen:

     
  1. User arrives, an event is called (either the default event if none is specified or the one explicitly called);
  2. Before that event is processed, I ensure that I have in session for this person:
    • a “loggedin” variable with either a value of true or false;
    • a user object, whether it’s empty or populated.
  3. Make those session values available to the rest of my app by placing them in the Event bucket
     
If I do accomplish the above checklist, then any template or process that cares about authentication will need only access the Event object or the Viewstate object (depending on whether it’s a view template as opposed to a controller or model object) in order to find out this user's status.

Okay, so how do I accomplish the above laundry list? Model-Glue comes pre-configured with a generic controller that has methods and listeners already set up to perform whatever action you want at the following times:
  • onRequestStart
  • onRequestEnd
  • onQueueComplete
When you first unpack your Model-Glue sample app, you'll see something similar to the following already present in Modelglue.XML:
<controllers>
    <controller name="GlobalController" type="controller.Controller">
        <message-listener message="OnRequestStart" function="OnRequestStart" />
        <message-listener message="OnQueueComplete" function="OnQueueComplete" />
        <message-listener message="OnRequestEnd" function="OnRequestEnd" />
    </controller>
</controllers>

This is the generic controller I was referring to, and the OnRequestStart listener and method is the one we'll be leveraging. Since OnRequestStart is called before the event itself is executed, it is the perfect place to do ensure that our default values are present and available to the rest of the app at all times.

Here is the onRequestStart method after I modified it to accommodate security:

<cffunction name="onRequestStart" access="public" returnType="void" output="false">
    <cfargument name="event" type="any">
    <cfif not structkeyexists(session,"user")>
        <cfset session.user = getUser().init() />
    </cfif>      
    <cfif not structkeyexists(session,"loggedin")>
        <cfset session.loggedin = "false" />
    </cfif>
    <cfset arguments.event.setValue("loggedin",session.loggedin) />
    <cfset arguments.event.setValue("user",session.user) />
</cffunction>

Here's what's happening.:

Before any event is executed, I am first checking for the existence of a "user" key in session. If it’s not there, I’m creating an empty instance of the user.cfc in my model (which was auto-wired in to this controller…you can see how that's done by checking out the sample app at the end of this post) and putting it into session. I’m then checking for the existence of a "loggedin" key in session, and if not there, creating it with a value of false. After this, I put both session values into the Event bucket and let the event that was called finish its execution.

THE LOGIN PROCESS

Okay, so now we have appropriate values present and available throughout the app. How about the actual logging in process? Let’s walk through it and see how the different parts are arranged. It’s pretty simple (and mostly familiar), you’ll see.

The Process:
  • User is presented with a login form whose action calls the “login” event;
  • The login event makes a broadcast to the authentication controller to perform it’s “login” method, username and password being present in the Event bucket since they were submitted via the login form;
  • The Authentication controller creates an empty instance of the user object, then calls the user object’s ‘login’ method, passing in the username and password arguments;
  • Internally to the User object, IF the user was found based on the credentials passed in, it populates its internal variables (firstname, lastname, id, etc.);
  • The authentication controller checks to see if the User object’s ID value is anything other than the default value of zero. If it IS, then authentication was successful. If it’s zero, authentication failed.
  • If authentication was successful, we put that instance of the user object into session, set session.loggedin equal to true, put both the user object and the loggedin value  into the Event bucket, and add a Result value of “success” so that our event will know what action to take.
  • If authentication failed, we put a message into the Event bucket indicating this (“login failed! Try again!”), and add a Result value of “failure”, again so that our event will know where to redirect to.
Here is my Authentication Controller's Login method:

<cffunction access="public" name="login" output="false" returntype="void" hint="I make the call to log a user in">
    <CFARGUMENT name="event" type="any">
    <cfset var objUser = getUser().init() />
    <cfset objUser.login(username = arguments.event.getValue("username"), password = arguments.event.getValue("password")) />
    <cfif objUser.getID() IS NOT 0>
        <cfset session.loggedin = true />
        <cfset session.user = objUser />
        <cfset arguments.event.setvalue("loggedin",true) />
        <cfset arguments.event.addResult("success") />
    <cfelse>
        <cfset arguments.event.setvalue("loggedin",false) />
        <cfset arguments.event.setvalue("message","Login failed! Try again.") />
        <cfset arguments.event.addResult("failure") />
    </cfif>
</cffunction>


SUMMARY

Those are the items of interest to understand and think about when doing basic authentication. Now, since I know that reading about a thing abstractly is not NEARLY the same as chewing on actual code, I zipped up the whole security skeleton app so you can download it and poke through its innards. It has nothing in it except for the basic security outlined above, but it DOES function (I'm using Querysim in my model).

Also, just for disclaimer purposes, I didn't invest a lot of time making it overly pretty, nor did I comment it as heavily as I normally would. I do believe, however, that it's a very good jumping off point for someone who has been wondering "how to think" about security in an MVC app.

Hope you find it useful!

Doug out.

NOTE 8/24/2009 - I realized long after I did this post that I actually coded in something in a wrong way: the way a new user bean is retrieved. I erroneously auto-wired in the user object to my controller, which causes multiple users to see each others information! What should happen is that the user bean must be gotten directly from the model. If you are using Coldspring (which I highly recommend), then the way you should get a new instance of the user object is by calling "getModelGlue.getConfigBean('user')". In your Coldspring.xml file, you will also need to make sure that you have the Singleton flag set to true, as in "<bean id="user" class="model.user" singleton="true" />". So, instead of this line:

getUser().init()

you would use this line:

getModelGlue.getConfigBean('user')

Sorry for the misinformation! :)



DOWNLOAD SECURITY SKELETON APP ZIP




Posted by dougboude at 3:54 AM | PRINT THIS POST! |Link | 13 comments
Subscription Options

You are not logged in, so your subscription status for this entry is unknown. You can login or register here.

Re: My Approach to Basic Security in a Model-Glue Application
Why bother copying the session variables into the event scope? Why not just keep them in session?
Posted by jeff on September 26, 2007 at 12:00 AM

Re: My Approach to Basic Security in a Model-Glue Application
Outstanding question Jeff. The answer is, because I need them available to my views, and views may NOT talk directly to session. The Event object is the common denominator between the M the V and the C, so I take them from session and put them into the Event bucket to ensure they are available for whatever part of my app needs them.
Posted by dougboude on September 26, 2007 at 12:10 AM

Re: My Approach to Basic Security in a Model-Glue Application
This is a very interesting post.

I found it while researching a bit for a project I have, maybe you can share you take on this.

* I have an application where there are multiple Roles, Basic user, Power Users and Admins.
* All can access pretty much the same pages but they are supposed to see/do different things on the page.
* An example is this : a simple Details Form.
* Basic Users can see everything on the form but cannot edit all the fields, just their names. All other fields are supposed to be read only.
* Power users land on the same page and they can pretty much update anything on the the form but they are not allowed to even see the 'Delete' button at the end of the page.
* Admins can create new data and can then come back to the form and update/delete anything they want. They can actually see the 'Delete' Button which deletes the whole record and enter notes on a TextArea only available to them.
* 6 months from now a new Role with a different set of access rules gets created.

How would you approach this? It is not a matter of some users see something and some see something else. All of them see basically the same stuff but with different levels of access Read Only, Edit, Edit/Delete, etc.

I'll play with the code you provided for this post and maybe I'll get a couple more ideas. In the meantime I'd be really interested in how you would do it.

I'm a beginner on ModelGLue and I'm finding a bunch of your posts really useful. Please keep up with the postings, they are a good resource.


Fernando
Posted by fernando lopez on November 3, 2007 at 12:55 AM

Re: My Approach to Basic Security in a Model-Glue Application
Fernando,

I would add on to my user object the ability to flag (preferably by Role, not by User): canEdit (bit), canAddDelete (bit). Now when I get my session back (session.user = objUser), I should have access to those bit settings. Adding to Doug's code above, if most of your app needs to see these security settings, then we can add them directly to the Event:

cfset arguments.event.setvalue("canEdit", objUser.getCanEdit())
cfset arguments.event.setvalue("canAddDelete", objUser.getCanAddDelete())

Now, those values are exposed to the views as well as the controllers, so in the view you can do this type of thing:

cfif viewState.getValue("canEdit")
-- show INPUT field --
cfelse
-- show raw data only, and maybe a HIDDEN input field for form handling --

For the delete button:

cfif viewState.getValue("canAddDelete")
-- show DELETE button --
cfelse
-- don't render DELETE button --

For maximum security, you can do the same tests in the objects, since they get arguments.Event in all cases: you can test in the Delete function whether the current user is even allowed to execute that command, say if he processed it through a hand-written URL to get around your hidden DELETE button.

HTH,
Jason
Posted by Jason on November 7, 2007 at 12:56 PM

Re: My Approach to Basic Security in a Model-Glue Application
Jason, That gives me a starting point on how to do things under Model-Glue.
As I said I'm still trying to learn the framework. While I do that our current applications need to keep working. At some point will move to a completely ModelGlue app, for now I'm fixing things under our old way of coding **read : spaghetti**.

I found that having users jumping directly to a page and bypassing the rendered button that triggers the action can be a serious problem. I do Template Level checks and validate the users rights to even access the template to delete.

I'll be posting something on my page once I'm done with our solution.

I'll keep in mind your advice once we start moving to MG and have to replicate what I'm developing right now, thanks for the pointers.

Fernando


@Doug : I try to login using http://www.dougboude.com/blog/login.cfm and I keep getting errors.
Posted by grtfercho on November 8, 2007 at 11:16 AM

Re: My Approach to Basic Security in a Model-Glue Application
So there's no need for a password in this example? Please pardon my noobieness, but I'm trying to get a handle on MG and came across your example. I see the password argument is required, but I seem to be able to log in without it.
Posted by Jon on January 3, 2008 at 11:13 AM

Re: My Approach to Basic Security in a Model-Glue Application
Thanks Doug for sharing this great example. I all ready apply it to one of my projects and your share makes it look greater and pro.

I have one question, I hope you can help me with: My app has a time out session of 30 minutes, and some parts of the site I use CFDIV and ColdFusion.navitate, etc. I foud out that if I leave the page open "inside" the Admin session if I click a "hard" link I get redirect to my login page, but if I click on an ajaxlink the content gets loaded and possible be edited. I was thinking how can I trigger some function when timeout session to cancel any possible processes if the page is leaved open inside an Admin session.

Thanks again.
Posted by Felipe Serrano on April 18, 2008 at 1:35 AM

Re: My Approach to Basic Security in a Model-Glue Application
Doug,

I am really a new beginner to coldfusion Model Glue. I found this article and it is helpful too. But could you please provide us with the real working application with the database file (.sql,..), login.cfm, register.cfm.... for this login authentication?

Your help is appreciated in advance.

Thank you.
Posted by pralen on June 27, 2008 at 2:25 AM

Re: My Approach to Basic Security in a Model-Glue Application
I downloaded your SECURITY SKELETON APP ZIP. can u help me with which files should I edit so that it runs? I have model glue installed already.

how to run the login page?
http://localhost:8500/SecuritySkeletonApp/index.cfm?event=login
I found this error:
Model-Glue: The xml file you're trying to include, somepath-to-my-server\www\config\security.xml, cannot be found.

The error occurred in C:\ColdFusion8\wwwroot\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 97


Sorry, I am a novice to Model Glue.. Please help.
Posted by pralen on June 27, 2008 at 2:48 AM

Re: My Approach to Basic Security in a Model-Glue Application
@pralen: FYI, the skeleton app is all there is; it uses querysim to simulate performing the actual authentication process, so there's no need to create any database tables for it to work. To make it real, you'd just replace the querysim portion with an actual query call to a database user table that has at least a username and password field.
Posted by dougboude on June 27, 2008 at 10:19 AM

Re: My Approach to Basic Security in a Model-Glue Application
I really appreciate the work on the Authentication Example. I have other MG projects running successfully but I cannot seem to quite get the example running. I am getting an error:

Bean creation exception in model.user
Could not find the ColdFusion Component model.user.:Please check that the given name is correct and that the component exists.

The error occurred in L:\CFusionMX7\wwwroot\coldspring\beans\BeanDefinition.cfc: line 391
Called from L:\CFusionMX7

I can see the user.cfc within the model directory and have checked it numerous times. I am looking forward to eventually executing this process within one of my apps so any guidance would be appreciated.

Jeff
Posted by Jeffrey Smith on July 10, 2008 at 2:00 AM

Re: My Approach to Basic Security in a Model-Glue Application
Hi Jeff. Looks to me like you just need to add the name of the parent directory to the class attribute in your coldspring file. For instance, if your app isn't running directly off of the root of your web server, the class would be "myapp.model.user" instead of just "model.user". Your error looks to me like this is exactly what the problem is. Experiment with the class path and see if that fixes it.
Doug :0)
Posted by dougboude on July 10, 2008 at 5:14 PM

Re: My Approach to Basic Security in a Model-Glue Application
Doug,

I appreciate your quick reply. It validated something I thought of after I wrote you but the affirmation was invaluable and the the resolution worked great. I am new to MG and your site has been a great resource!

Regards,
Jeff
Posted by Jeffrey Smith on July 10, 2008 at 11:53 PM

Name:   Required
Email:   Required your email address will not be publicly displayed.

Want to receive notifications when new comments are added? Login/Register for an account.

Time to take the Turing Test!!!

2 plus 16 equals
Type in the answer to the question you see above:

Your comment:

Sorry, no HTML allowed!