By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,544 Members | 2,002 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,544 IT Pros & Developers. It's quick & easy.

Problem Creating a User Object

100+
P: 113
Hi,

I've been trying to implement a more OOP oriented approach to dealing with user security on one of my websites, and I am trying to validate the user against an array of roles, however I am struggling with a type error:

Expand|Select|Wrap|Line Numbers
  1.  The argument ROLES passed to function setRoles() is not of type array.
  2. If the component name is specified as a type of this argument, the reason for this error might be that a definition file for such component cannot be found or is not accessible.
  3.  
  4. The error occurred in D:\***\***\AppTest\com\user.cfc: line 50
  5.  
  6. 48 :     <cffunction name="getRoles" access="public" output="true" returntype="array">
  7. 49 :         <cfreturn variables.roles />
  8. 50 :     </cffunction>
  9. 51 : 
  10. 52 :     <cffunction name="setRoles" access="public" output="false" returntype="void">
  11.  

I am definately passing in an array, or at least a list which I think are basically the same thing? Here is my code for the instantiating the User object (contained in /index.cfm):

Expand|Select|Wrap|Line Numbers
  1.     if(NOT isDefined("Session.user")) {
  2.         Request.Ses.user = createObject("component","com.user").init(Request.App.Dsn);
  3.         Request.Ses.user.setUsername("eddy");
  4.         Request.Ses.user.setPassword("eddy");            
  5.         Request.Ses.user.setRoles("staff, user");                    
  6.     }
  7.  
and here is the User.cfc object:

Expand|Select|Wrap|Line Numbers
  1. <!--- security component --->
  2. <cfcomponent displayname="user" output="false" hint="Provides authentication for users">
  3.     <cffunction name="init" access="public" output="false" returntype="user" hint="Constructor for this CFC">
  4.         <!--- take DSN as argument --->
  5.         <cfargument name="dsn" type="string" required="true" hint="The datasource name" />               
  6.         <!--- put dsn in variables scope so we can use it throughout the CFC --->
  7.         <cfscript>
  8.             // datasource
  9.             variables.dsn = arguments.dsn;
  10.             // user details
  11.             variables.username = "";            
  12.             variables.password = "";
  13.             variables.loginStatus = 0;
  14.             variables.roles = ArrayNew(1);
  15.         </cfscript>
  16.         <!--- return this CFC --->
  17.         <cfreturn this />
  18.     </cffunction>
  19.  
  20.     <!--- Object data functions --->
  21.     <cffunction name="getUsername" access="public" output="false" returntype="string">
  22.         <cfreturn variables.username />
  23.     </cffunction>
  24.  
  25.     <cffunction name="setUsername" access="public" output="false" returntype="void">
  26.         <cfargument name="username" type="string" required="true" />
  27.         <cfset variables.username = arguments.username />
  28.     </cffunction>
  29.  
  30.     <cffunction name="getPassword" access="private" output="false" returntype="string">
  31.         <cfreturn variables.password />
  32.     </cffunction>
  33.  
  34.     <cffunction name="setPassword" access="public" output="false" returntype="void">
  35.         <cfargument name="password" type="string" required="true" />
  36.         <cfset variables.password = arguments.password />
  37.     </cffunction>
  38.  
  39.     <cffunction name="getLoginStatus" access="private" output="false" returntype="string">
  40.         <cfreturn variables.loginStatus />
  41.     </cffunction>
  42.  
  43.     <cffunction name="setLoginStatus" access="public" output="false" returntype="void">
  44.         <cfargument name="loginStatus" type="string" required="true" />
  45.         <cfset variables.loginStatus = arguments.loginStatus />
  46.     </cffunction>
  47.  
  48.     <cffunction name="getRoles" access="public" output="true" returntype="array">
  49.         <cfreturn variables.roles />
  50.     </cffunction>
  51.  
  52.     <cffunction name="setRoles" access="public" output="false" returntype="void">
  53.         <cfargument name="roles" type="array" required="true" />
  54.         <cfset variables.roles = arguments.roles />
  55.     </cffunction>         
  56.  
  57.     <!--- C,R,U,D Methods --->
  58.     <cffunction name="read" access="public" output="false" returntype="Void" hint="CRUD Method"> 
  59.         <cfargument name="user" type="user" required="yes" />
  60.         <cfargument name="username" type="string" required="yes" />
  61.  
  62.         <cfset var qread = 0 />
  63.  
  64.         <cfquery name="qRead" datasource="#variables.dsn#">
  65.             SELECT
  66.             username, roles
  67.             FROM cms_security
  68.             WHERE
  69.             username = <cfqueryparam value="#variables.getUsername()#" 
  70.                             cfsqltype="cf_sql_varchar" />
  71.         </cfquery>
  72.         <cfif qRead.RecordCount>
  73.             <cfset arguments.User.setRoles(qRead.roles) />
  74.             <cfelse>
  75.                 <cfthrow type="emptyRecordset" errorcode="User.read.emptyRecordset" message="User with name #arguments.username# not found" />
  76.         </cfif>
  77.     </cffunction>
  78.  
  79.     <!--- Persistent data functions --->
  80.     <cffunction name="checkRoles" access="public" output="false" returntype="boolean" hint="Checks user roles against a supplied array or list">
  81.  
  82.         <!--- Initialise variables --->
  83.         <cfset var results = StructNew() />
  84.         <cfset var qCheckRoles = 0 />
  85.  
  86.         <!--- defaults --->
  87.         <cfset results.success = true />
  88.         <cfset results.message = "The user has been validated." />        
  89.  
  90.         <cftry>
  91.             <cfquery name="qCheckRoles" datasource="#variables.dsn#">
  92.                 SELECT roles
  93.                 FROM cms_security
  94.                 WHERE username = <cfqueryparam value="#variables.getUsername()#" 
  95.                         cfsqltype="cf_sql_varchar" />
  96.             </cfquery>
  97.             <cfcatch type="database">
  98.                 <cfset results.success = false />
  99.                 <cfset results.message = "User role check failed.  The error details if available 
  100.                     are as follows: " & CFCATCH.Detail />
  101.             </cfcatch>
  102.         </cftry>
  103.  
  104.         <!--- if we got data back, initialize the object --->
  105.         <cfif IsQuery(qCheckRoles) AND qCheckRoles.RecordCount EQ 1>
  106.             <cfdump var="#qCheckRoles#">
  107.             <cfreturn true>
  108.         <cfelse>
  109.             <cfreturn false>
  110.         </cfif>
  111.  
  112.     </cffunction>     
  113.     <cffunction name="validate" access="public" output="false" returntype="boolean" hint="validates username and password">
  114.  
  115.         <!--- Initialise variables --->
  116.         <cfset var results = StructNew() />
  117.         <cfset var qValidate = 0 />
  118.  
  119.         <!--- defaults --->
  120.         <cfset results.success = true />
  121.         <cfset results.message = "The user has been validated." />        
  122.  
  123.         <cftry>
  124.             <cfquery name="qValidate" datasource="#variables.dsn#">
  125.                 SELECT username,password
  126.                 FROM cms_security
  127.                 WHERE username = <cfqueryparam value="#variables.getUsername()#" 
  128.                         cfsqltype="cf_sql_varchar" />
  129.                 AND password = '#hash(variables.password)#'
  130.             </cfquery>
  131.             <cfcatch type="database">
  132.                 <cfset results.success = false />
  133.                 <cfset results.message = "The person insert failed.  The error details if available 
  134.                     are as follows: " & CFCATCH.Detail />
  135.             </cfcatch>
  136.         </cftry>
  137.  
  138.         <!--- if we got data back, initialize the object --->
  139.         <cfif IsQuery(qValidate) AND qValidate.RecordCount EQ 1>        
  140.             <cfset setLoginStatus(true) />
  141.             <cfreturn true>
  142.         <cfelse>
  143.             <cfreturn false>
  144.         </cfif>
  145.  
  146.     </cffunction>     
  147. </cfcomponent>
  148.  
Could someone point out where I am going wrong?

Thanks,

Chromis
Jun 11 '08 #1
Share this Question
Share on Google+
14 Replies


acoder
Expert Mod 15k+
P: 16,027
You're passing a list rather than an array. What you could do is accept a string as the input and convert to an array in the function, or create the array with cfset and pass that to setRoles.
Jun 11 '08 #2

100+
P: 113
You're passing a list rather than an array. What you could do is accept a string as the input and convert to an array in the function, or create the array with cfset and pass that to setRoles.
Ah right ok I'll do that. Thanks alot for your help, I'll no doubt have some questions shortly!
Jun 11 '08 #3

acoder
Expert Mod 15k+
P: 16,027
No problem, if you do, you know where to come :)
Jun 11 '08 #4

100+
P: 113
No problem, if you do, you know where to come :)
Ok I'm getting a little confused, basically I want to create an OOP method for validating a user, the user must have a valid username, password and also have one of the roles for the particular page that they are viewing.

I've got the fundamentals of the validation working now, though i'm sure i'm not getting the structure of the User.cfc quite right. I've read about OOP and it talks about DAOs and Gateways I have tryed to implement this sort of structure. Tho I have struggled to separate the database calls into a Gateway file.
At the moment i seem to have a kind of mis-match of the two in one file. Could you tell me how i could improve this code?

Instantiation and data output (/index.cfm):

Expand|Select|Wrap|Line Numbers
  1. <cfscript>
  2.     if(NOT isDefined("Session.user")) {
  3.         Request.Ses.user = createObject("component","com.user").init(Request.App.Dsn);
  4.         Request.Ses.user.setUsername("eddy");
  5.         Request.Ses.user.setPassword("eddy");                                    
  6.     }
  7. </cfscript>
  8.  
  9. <cfdump var="#Request#">
  10.  
  11. <cfoutput>Validate result: #Request.Ses.user.validate()#</cfoutput>
  12. Roles: <cfoutput>#Request.Ses.user.getRoles()#</cfoutput>
  13. Actual Roles:<cfoutput>#Request.Ses.user.getActualRoles()#</cfoutput>
  14. Validate roles: <cfoutput>#Request.Ses.user.checkRoles("staff,user")#</cfoutput>
  15.  
User DAO / Gateway (/com/User.cfc):

Expand|Select|Wrap|Line Numbers
  1. <!--- security component --->
  2. <cfcomponent displayname="user" output="false" hint="Provides authentication for users">
  3.     <cffunction name="init" access="public" output="false" returntype="user" hint="Constructor for this CFC">
  4.         <!--- take DSN as argument --->
  5.         <cfargument name="dsn" type="string" required="true" hint="The datasource name" />               
  6.         <!--- put dsn in variables scope so we can use it throughout the CFC --->
  7.         <cfscript>
  8.             // datasource
  9.             variables.dsn = arguments.dsn;
  10.             // user details
  11.             variables.username = "";            
  12.             variables.password = "";
  13.             variables.loginStatus = 0;
  14.             variables.roles = "";
  15.             variables.actualRoles = "";            
  16.         </cfscript>
  17.         <!--- return this CFC --->
  18.         <cfreturn this />
  19.     </cffunction>
  20.  
  21.     <!--- Object data functions --->
  22.     <cffunction name="getUsername" access="public" output="false" returntype="string">
  23.         <cfreturn variables.username />
  24.     </cffunction>
  25.  
  26.     <cffunction name="setUsername" access="public" output="false" returntype="void">
  27.         <cfargument name="username" type="string" required="true" />
  28.         <cfset variables.username = arguments.username />
  29.     </cffunction>
  30.  
  31.     <cffunction name="getPassword" access="private" output="false" returntype="string">
  32.         <cfreturn variables.password />
  33.     </cffunction>
  34.  
  35.     <cffunction name="setPassword" access="public" output="false" returntype="void">
  36.         <cfargument name="password" type="string" required="true" />
  37.         <cfset variables.password = arguments.password />
  38.     </cffunction>
  39.  
  40.     <cffunction name="getLoginStatus" access="private" output="false" returntype="string">
  41.         <cfreturn variables.loginStatus />
  42.     </cffunction>
  43.  
  44.     <cffunction name="setLoginStatus" access="public" output="false" returntype="void">
  45.         <cfargument name="loginStatus" type="string" required="true" />
  46.         <cfset variables.loginStatus = arguments.loginStatus />
  47.     </cffunction>
  48.  
  49.     <cffunction name="getRoles" access="public" output="true" returntype="string">
  50.         <cfreturn variables.roles />
  51.     </cffunction>
  52.  
  53.     <cffunction name="setRoles" access="public" output="false" returntype="void">
  54.         <cfargument name="roles" type="string" required="true" />
  55.         <cfset variables.roles = arguments.roles />
  56.     </cffunction>         
  57.  
  58.     <!--- Persistent data functions --->
  59.     <cffunction name="checkRoles" access="public" output="false" returntype="boolean" hint="Checks user roles against a supplied array or list">
  60.         <cfargument name="roles" type="string" required="true" />
  61.  
  62.         <!--- Initialise variables --->
  63.         <cfset var results = StructNew() />
  64.         <cfset var qCheckRoles = 0 />
  65.  
  66.         <!--- defaults --->
  67.         <cfset results.success = true />
  68.         <cfset results.message = "The user has been validated." />        
  69.  
  70.         <cftry>
  71.             <cfquery name="qCheckRoles" datasource="#variables.dsn#">
  72.                 SELECT roles
  73.                 FROM cms_security
  74.                 WHERE username = <cfqueryparam value="#variables.getUsername()#" 
  75.                         cfsqltype="cf_sql_varchar" />
  76.             </cfquery>
  77.             <cfcatch type="database">
  78.                 <cfset results.success = false />
  79.                 <cfset results.message = "User role check failed.  The error details if available 
  80.                     are as follows: " & CFCATCH.Detail />
  81.             </cfcatch>
  82.         </cftry>
  83.  
  84.         <!--- if we got data back, check roles --->
  85.         <cfif IsQuery(qCheckRoles) AND qCheckRoles.RecordCount EQ 1>
  86.  
  87.             <cfset variables.userRoles = ValueList(qCheckRoles.roles,",")>
  88.  
  89.             <cfloop list="#variables.userRoles#" index="role">
  90.                 <cfif ListFindNoCase(arguments.roles,role) GT 0>
  91.                     <cfset roleFound = true>
  92.                 </cfif>
  93.             </cfloop>
  94.  
  95.             <cfif roleFound EQ true>
  96.                 <cfreturn true>
  97.             <cfelse>
  98.                 <cfreturn false>
  99.             </cfif>
  100.  
  101.         <cfelse>
  102.             <cfreturn false>
  103.         </cfif>
  104.  
  105.     </cffunction>     
  106.     <cffunction name="validate" access="public" output="false" returntype="boolean" hint="validates username and password">
  107.  
  108.         <!--- Initialise variables --->
  109.         <cfset var results = StructNew() />
  110.         <cfset var qValidate = 0 />
  111.  
  112.         <!--- defaults --->
  113.         <cfset results.success = true />
  114.         <cfset results.message = "The user has been validated." />        
  115.  
  116.         <cftry>
  117.             <cfquery name="qValidate" datasource="#variables.dsn#">
  118.                 SELECT username,password
  119.                 FROM cms_security
  120.                 WHERE username = <cfqueryparam value="#variables.getUsername()#" 
  121.                         cfsqltype="cf_sql_varchar" />
  122.                 AND password = '#hash(variables.password)#'
  123.             </cfquery>
  124.             <cfcatch type="database">
  125.                 <cfset results.success = false />
  126.                 <cfset results.message = "The person insert failed.  The error details if available 
  127.                     are as follows: " & CFCATCH.Detail />
  128.             </cfcatch>
  129.         </cftry>
  130.  
  131.         <!--- if we got data back, initialize the object --->
  132.         <cfif IsQuery(qValidate) AND qValidate.RecordCount EQ 1>        
  133.             <cfset setLoginStatus(true) />
  134.             <cfreturn true>
  135.         <cfelse>
  136.             <cfreturn false>
  137.         </cfif>
  138.  
  139.     </cffunction>     
  140. </cfcomponent>
  141.  
The parts I want to look at specifically are the checkRoles and validate methods. Should I put them into one method or attempt to separate the checkRoles and validate database calls into two different methods and have one validate method? I'm a bit confused :)

Thanks,

Chromis
[code]
Jun 11 '08 #5

acoder
Expert Mod 15k+
P: 16,027
It probably would be a good idea to put the database calls in its own object which you can instantiate/call when the user object is initialised. Then it'd smply be a function call to make the query which would return true/false or a result struct. As for the validate() and checkRoles() functions, it depends on whether you ever need only the checkRoles function. If you do, you can call checkRoles within validate should validate() also validate roles.
Jun 11 '08 #6

100+
P: 113
It probably would be a good idea to put the database calls in its own object which you can instantiate/call when the user object is initialised. Then it'd smply be a function call to make the query which would return true/false or a result struct. As for the validate() and checkRoles() functions, it depends on whether you ever need only the checkRoles function. If you do, you can call checkRoles within validate should validate() also validate roles.
Thanks acoder, I'll have a go and post back what i come up with.
Jun 12 '08 #7

100+
P: 113
Hi acoder,

Ok, I'm had some time to look at this code again and I think what I need to do is create a UserDAO.cfc for my persistent data functions and then pass the user.cfc object to the UserDAO.cfc object upon initialisation, I can then authenticate and check the roles using UserDAO, is this right?

On various pages I'll need to the check the user roles against the role for that page and then redirect the user to another page, do I need to create another object for this, security.cfc for instance? My thinking is that the user objects shouldn't really be used for this sort of thing, as it's not a user objects responsibility to perform the business logic.

Thanks,

Chromis
Aug 5 '08 #8

acoder
Expert Mod 15k+
P: 16,027
That's right, though you could have a method in user.cfc which performs this logic, i.e. checks that the user has the role for that page; if not, redirect.
Aug 5 '08 #9

100+
P: 113
Ok, I started going down the route of creating a separate object (security.cfc) to deal with the business logic of redirecting the user, but that got me into the realm of keeping that object persistent aswell which seemed really overkill. In the end I took your advice and put a protect function in the user.cfc to redirect the user to a different page if the roles for the user were not found. This is definately the easiest method and for this project not one that's going to cause any problems so I guess I'll stick with it.

I guess what I'm trying to find out whilst learning OOP is when to separate logic and when not to, alot of what I have read so far talks about making one object do one thing well and what felt wrong about the user.cfc having a protect function and also the database interaction functions, was that it was no longer just defining a user, but providing functions to deal with it within the application.
Could you give me any advice regarding this?

Anyway here's my protect statement which I'm putting in my index.cfm's, if the user does not have the specified role record in the database, the user will be redirected to the specified URL.

index.cfm
Expand|Select|Wrap|Line Numbers
  1.     Request.Ses.User.protect("staff","../index.cfm");
  2.  
User.cfc - protect function.
Expand|Select|Wrap|Line Numbers
  1.     <cffunction name="protect" access="public" output="true" returntype="boolean" hint="Checks user roles against a supplied array or list">
  2.         <cfargument name="roles" type="string" required="true" />
  3.         <cfargument name="redirectURL" type="string" required="true" />    
  4.  
  5.         <cfif variables.checkRoles(arguments.roles) EQ true>
  6.          <cflocation url="#arguments.redirectURL#">
  7.             <cfreturn true>
  8.         <cfelse>
  9.             <cflocation url="#arguments.redirectURL#">
  10.             <cfreturn false>
  11.         </cfif>
  12.  
  13.     </cffunction>
  14.  
Again thanks for your help!
Aug 5 '08 #10

acoder
Expert Mod 15k+
P: 16,027
I see what you mean. Coldfusion doesn't place any restrictions in terms of OOP, though this may change in future versions.

You could look at frameworks such as Fusebox/Mach-II which might help. You could also look into the MVC (Model-View-Controller) pattern for separation.
Aug 5 '08 #11

100+
P: 113
Ok well that's put me at ease a bit. I have looked into machII and the MVC pattern, I've only developed a couple of tests so far. I have a few questions regarding this though:

1. At what scale of application does the machII begin to be useful?
2. Is it good for rapid application development?
3. Does it lend itself well to OOP techniques?
4. What do you develop with?
Aug 6 '08 #12

acoder
Expert Mod 15k+
P: 16,027
Mach-II is probably better suited for large, complex applications. Another alternative is Model-Glue which can be simpler. Both enforce MVC whereas Fusebox doesn't. You may find this article/slideshow useful which compares the three.

What do I use? I don't use any, though I probably should. If I was developing from the ground up, I would and I'd change my programming style too.
Aug 6 '08 #13

100+
P: 113
Thanks I'll take a look at that. I guess for the app i'm developing at the moment I won't be needing a framework. But from what I have seen so far I think MachII would be my choice.

Again thanks for you help i'll no doubt be asking more questions soon!
Aug 7 '08 #14

acoder
Expert Mod 15k+
P: 16,027
No problem. This is an interesting discussion probably not covered in this forum before. Fire away when the need comes and I'll see what I can do :)
Aug 7 '08 #15

Post your reply

Sign in to post your reply or Sign up for a free account.