I have a Function in CFC file, which will be called from .cfm file like below
<cffunction name="cftest" access="public" returntype="query" output="true" hint="Function returns Records">
<cfquery name="qryTest" datasource="DBTest">
select * from emp_tab;
</cfquery>
<cfreturn selectRecordsResultSet />
</cffunction>
How can I handle DB Exception using cftry? as this is returning Query, Is it possible to catch the DB Exception and pass the Details to the other page from where it is called?
Thanks
Here is an example of my usual implementation of this:
<cffunction name="getCurrentRecordsCount" access="public" output="false" returntype="any" hint="Get total history records count">
<cfargument name="filters" type="struct" required="false" default="#StructNew()#" hint="Filtering rules">
<cfset var qGetRecordCount = "" />
<cftry>
<cfquery datasource="#variables.dsn#" name="qGetRecordCount">
SELECT COUNT(*) AS cnt FROM ....
</cfquery>
<cfreturn qGetRecordCount.cnt />
<cfcatch type="any">
<cfreturn error(cfcatch.message, cfcatch.detail) />
</cfcatch>
</cftry>
</cffunction>
If you want to handle only database errors, change type to the database.
Errors handling and reporting performed using these three methods:
<cffunction name="error" access="private" output="false" returntype="boolean" hint="Set error status and message">
<cfargument name="message" type="string" required="true" hint="Error message text" />
<cfargument name="detail" type="string" required="false" default="" hint="Error detail text" />
<cfset variables.fError = true />
<cfset variables.fErrorText = arguments.message />
<cfif Len(arguments.detail)>
<cfset variables.fErrorText = variables.fErrorText & " [" & arguments.detail & "]" />
</cfif>
<cfreturn false />
</cffunction>
<cffunction name="gotError" access="public" output="false" returntype="boolean" hint="Return latest error flag state">
<cfreturn variables.fError />
</cffunction>
<cffunction name="getError" access="public" output="false" returntype="string" hint="Return latest error text and reset the flag">
<cfset var txt = variables.fErrorText />
<cfset variables.fError = false />
<cfset variables.fErrorText = "" />
<cfreturn txt />
</cffunction>
Please note that for methods with returntype="void" I use cfset instead of cfreturn:
<cfset error(cfcatch.message, cfcatch.detail) />
So in code I can do the following (cfscript):
// calculate filtered records count
totalLogCount = request.loggingService.getCurrentRecordsCount(filters);
// check if error was thrown
if (request.loggingService.gotError()) {
// report the error details somehow
WriteOutput(request.loggingService.getError());
}
Related
I have following code that first creates an object and afterwards it calls a function called init.
<cfset SESSION.objWatchlist = createObject("component", "path") />
<cfset SESSION.objWatchlist.init(session.addressid) />
So far so good. The init function looks like this:
<cffunction name="init" access="remote" output="false">
<cfargument name="addressID" type="numeric" required="true" />
<cfset THIS.ADDRESSID = arguments.addressID />
<cfset THIS.WATCHLIST = arrayNew(1) />
<cfset initWatchlistArray() />
<cfreturn this />
</cffunction>
Now I want to add another argument to the cffunction. I will call it like this:
<cfset SESSION.objWatchlist.init("addressid", session.addressid) />
and
<cfset SESSION.objWatchlist.init("sessionid", session.sessionid) />
I add some new lines in the cffunction:
<cffunction name="init" access="remote" output="false">
<cfargument name="typeOfID" type="string" required="true" />
<cfif arguments.typeOfID eq "addressid">
<cfargument name="addressID" type="numeric" required="true" />
<cfset THIS.ADDRESSID = arguments.addressID />
<cfelseif arguments.typeOfID eq "sessionid">
<cfargument name="sessionID" type="string" required="true" />
<cfset THIS.SESSIONID = arguments.sessionID />
</cfif>
<cfset THIS.WATCHLIST = arrayNew(1) />
<cfset initWatchlistArray() />
<cfreturn this />
</cffunction>
But everytime I get the error "The tag must be nested inside a CFFUNCTION tag".
Now my question is how can I write a function in which the second argument is based on the first one?
Is this even possible?
Thank you.
At the end I have found a solution that looks like that:
First I call the function:
<cfset SESSION.objWatchlist.init(addressID=session.addressid) />
<cfset SESSION.objWatchlist.init(sessionID=session.sessionid) />
I changed the function to following:
<cffunction name="init" access="remote" output="false">
<cfargument name="addressID" type="numeric" required="false" />
<cfargument name="sessionID" type="string" required="false" />
<cfif isDefined("arguments.addressid")>
<cfset THIS.ADDRESSID = arguments.addressID />
<cfelseif isDefined("arguments.sessionid")>
<cfset THIS.SESSIONID = arguments.sessionid />
</cfif>
<cfset THIS.WATCHLIST = arrayNew(1) />
<cfset initWatchlistArray() />
<cfreturn this />
</cffunction>
When calling the function I can call them with the name of the argument in the cffunction.
Afterwards I can check which one is defined.
This is too long for a comment, but I have a little cleanup on this
SESSION.objWatchlist.init(addressID=session.addressid);
SESSION.objWatchlist.init(sessionID=session.sessionid);
any function init(numeric addressID, string sessionid) access="remote" output="false" {
if (arguments.keyexists(("addressid")) { THIS.ADDRESSID = arguments.addressID; }
else if (arguments.keyexists("sessionid") { THIS.SESSIONID = arguments.sessionid; }
THIS.WATCHLIST = [];
initWatchlistArray();
return this;
}
Note: the use of .keyexists(). This is a faster check because it does not have to look through every scope that could have something that matches.
Also note it is easier to blank out an array.
I have a query running to gather an adjacency list and then generate an XML object of that list as a tree. Next I need to output that tree as a simple HTML.
I would like my output of XML document to be:
<ul>
<li margin="5">Title
<ul>
<li margin="10">Title</li>
</ul>
</li>
</ul>
Here is what I have coded so far:
<cfquery name="nodeTable" datasource="#database.ds#">
SELECT [mc_location].[id],[mc_location].[title], [mc_location].[parent_id] FROM [mc_location]
LEFT JOIN [mc_location_type] ON [mc_location].[id] = [mc_location_type].[location_id]
WHERE [mc_location_type].[category] = 'staff'
</cfquery>
<cffunction name="outputChildNodes" access="public" returntype="void" output="true">
<cfargument name="nodeTable" type="query" required="true" hint="I am the node query object."/>
<cfargument name="parent_id" type="numeric" required="false" default="0"/>
<cfset var local = {}/>
<cfquery name="local.childNodes" dbtype="query">
SELECT id, parent_id, title
FROM arguments.nodeTable
WHERE parent_id = <cfqueryparam value="#arguments.parent_id#" cfsqltype="cf_sql_integer" />
ORDER BY id ASC
</cfquery>
<cfloop query="local.childNodes">
<child id="#local.childNodes.id#" parent-id="#local.childNodes.parent_id#" name="#local.childNodes.title#">
<cfset outputChildNodes(arguments.nodeTable, local.childNodes.id)/>
</child>
</cfloop>
<cfreturn/>
</cffunction>
<!--- Build the node XML document recursively. --->
<cfxml variable="nodeTree">
<childern>
<!--- Output the root-level nodes. --->
<cfset outputChildNodes( nodeTable ) />
</childern>
</cfxml>
<!--- Render the XML document. --->
<cfloop index="childern" array="#nodeTree.childern#">
<cfloop index="child" array="#childern#">
<cfif isStruct(child.XmlAttributes)>
<cfdump var="#child[1].XmlAttributes#"/>
</cfif>
</cfloop>
</cfloop>
Thank you!
The problem I was experiencing had to do with not properly defining variable scopes (e.g. using "var" in cfset, using arguments.parameter, etc.), which was necessary in order to recursively call a function. Presented below is a brief solution.
<!--- query for parent child tree --->
<cfquery name="tree_nodes" datasource="#sonis.ds#">
SELECT [location].[id],[location].[title], [location].[parent_id] FROM [location]
LEFT JOIN [location_category] ON [location].[id] = [location_category].[location_id]
WHERE [location_category].[category] = 'staff'
</cfquery>
<!--- build tree from adjacency list function --->
<cffunction name="build_tree" access="public" output="true">
<cfargument var name="tree_nodes" type="query" required="true"/>
<cfargument var name="parent_id" type="numeric" required="false" default="0"/>
<cfargument var name="depth" type="numeric" required="false" default="0"/>
<cfset var local = {}/>
<cfquery name="local.child_node" dbtype="query">
SELECT id, parent_id, title
FROM arguments.tree_nodes
WHERE parent_id = <cfqueryparam value="#arguments.parent_id#" cfsqltype="cf_sql_integer" />
ORDER BY id ASC
</cfquery>
<cfset var branch = {}/>
<cfset var counter = 1/>
<cfloop query="local.child_node">
<cfset local.depth = arguments.depth/>
<cfset branch[counter++] = {
'id' = '#local.child_node.id#',
'title' = '#local.child_node.title#',
'parent_id' = '#local.child_node.parent_id#',
'depth' = local.depth,
'children' = build_tree(arguments.tree_nodes, local.child_node.id,++local.depth)
} />
</cfloop>
<cfreturn branch/>
</cffunction>
<!--- print tree as select box function --->
<cffunction name="print_tree_select" access="public" output="true">
<cfargument var name="tree" type="struct" required="true"/>
<cfargument var name="selected" type="numeric" required="false" default="0"/>
<cfargument var name="child" type="numeric" required="false" default="0"/>
<cfif child eq '0'><select name="select_tree"><option value="null"></option></cfif>
<cfloop from="1" to="#StructCount(arguments.tree)#" index="a">
<option value="#arguments.tree[a]['id']#"<cfif #arguments.selected# eq #arguments.tree[a]['id']#> selected</cfif>>
<cfif #arguments.tree[a]['depth']# GT 0>
#RepeatString('--', arguments.tree[a]['depth'])#
</cfif>
#arguments.tree[a]['title']#
</option>
<cfif StructKeyExists(arguments.tree[a], 'children') AND StructCount(arguments.tree[a]['children']) GT 0>
#print_tree_select(arguments.tree[a]['children'],arguments.selected, 1)#
</cfif>
</cfloop>
<cfif child eq '0'></select></cfif>
</cffunction>
<!--- print tree as list function --->
<cffunction name="print_tree_list" access="public" output="true">
<cfargument var name="tree" type="struct" required="true"/>
<ul style="list-style-type: circle;">
<cfloop from="1" to="#StructCount(arguments.tree)#" index="local.i">
<li>
<cfform method="post" name="edit">
#arguments.tree[local.i]['title']#
<cfinput type="hidden" name="id" value="#arguments.tree[local.i]['id']#"/>
<cfinput type="Submit" name="command" value="Edit"/>
</cfform>
<cfif StructKeyExists(arguments.tree[local.i], 'children') AND StructCount(arguments.tree[local.i]['children']) GT 0>
#print_tree_list(arguments.tree[local.i]['children'])#
</cfif>
</li>
</cfloop>
</ul>
</cffunction>
working on REST web-service, i don't have much experience coldfusion web-services.It is very basic web-service.Please if you guys can point me, what im doing wrong. it will be great help.
Application Server : Lucee 4.5.2.018 (Linux)
Please find below my code.
Component-Function/ Web-Service.
<cfcomponent rest="true" restpath="/hello">
<cffunction name="formPost" access="remote" returnType="struct" httpMethod="POST" restPath="/name" hint="POST Method" produces="application/json">
<cfargument name="firstname" type="String" restArgSource="Form">
<cfargument name="lastname" type="String" restArgSource="Form">
<cfset myStruct = structnew()>
<cfset myStruct.FirstName = firstname>
<cfset myStruct.LastName = lastname>
<cfquery name="Qry" datasource="myDSN">
select col1,col2 from myTableData
</cfquery>
<cfset myJsonVar = serializeJSON(Qry) />
<cfreturn myJsonVar>
</cffunction>
</cfcomponent>
Calling web-service
<cfhttp url="http://mydev:8888/rest/Example/hello/name" method="POST" result="res" port="8888" >
<cfhttpparam type="header" name="Accept" value="application/json">
<cfhttpparam type="formfield" name="firstname" value="Dan">
<cfhttpparam type="formfield" name="lastname" value="Gates">
</cfhttp>
<cfdump var="#res#">
Problem:
When defining returnType="struct" Error string can't cast String [{"COLUMNS":["COL1","COL2"],"DATA":[["0","7777777"],["0","888888"]]}] to a value of type [struct]
When defining returnType="string" No error coming "{\"COLUMNS\":[\"COL1\",\"COL2\"],\"DATA\":[[\"0\",\"7777777\"],[\"0\",\"888888\"]]}"
Trying get [DATA] values in loop
<cfloop from="1" to="#ArrayLen(d.DATA)#" index="i">
<cfloop from="1" to=#ArrayLen(d.DATA[i])# index="j">
<cfset resultSrt =d.COLUMNS[j]&" = " &d.DATA[i][j]>
#resultSrt#<br>
</cfloop>
</cfloop>
Message: No matching property [DATA] found in [string]
Stacktrace:The Error Occurred in
/opt/lucee/tomcat/webapps/ROOT/calling.cfm: line 52
50:
51:
52: <cfloop from="1" to="#ArrayLen(d.DATA)#" index="i">
53: <cfloop from="1" to=#ArrayLen(d.DATA[i])# index="j">
54: <cfset resultSrt =d.COLUMNS[j]&" = " &d.DATA[i][j]>
First of all since you are returning a query, you should set returnType to Query.
If you have set produces attribute of cffunction to application/json, in that case you don't need to perform explicit JSON serialization while returning data. ColdFusion will automatically do it for you. You can just write:
<cfreturn Qry />
To read the result returned from the service you need to deserialize the data. Like this:
<cfdump var="#deserializeJson(res.filecontent)#">
I'm trying to make a form that accesses a MySQL database. But all of this is new to me. I think I might be trying to reach too far beyond my basic understanding of this. Here is my question.
I have a form. In this form I have the connection made to the database and am able to post to the database. What I'm trying to do is have an option field with "tile 1", "tile 2", "tile 3". For each "tile" I'd like to have the rest of the fields saved under that tile ID. I'd also like to be able to pull that tiles information when selecting the tile from the option field.
Here is my current code. Please keep in mind I'm new to all of this. Thanks.
Form:
<cfform method="post" name="TileAdAdmin" action="index.cfm">
<cfoutput>
<table width="500px">
<tr>
<td height="159"><label class="labelStyle">Tile Ads</label></td>
<td>
Tile Number:<select name="TAID" id="TAID">
<option value="0">None</option>
<option value="1">Tile 1</option>
<option value="2">Tile 2</option>
<option value="3">Tile 3</option>
</select><br /><br />
Headline:
<input name="TAHL" type="text" id="TAHL" title="TAHL" value="#variables.ta.TAHL#" maxlength="30" />
<br />
Image Name:
<input name="TAImage" type="text" id="TAImage" title="TAImage" value="#variables.ta.TAImage#" maxlength="30" />
<br />
Discription:
<textarea name="TADiscription" cols="30" rows="6" id="TADiscription" title="VimeoID">#variables.ta.TADiscription#</textarea> <br />
</td>
</tr>
</table><br />
<input type="hidden" value="#variables.controller#" name="controller">
<input type="hidden" value="#variables.action#" name="action">
<input type="Submit" value="Submit" name="Submit" class="formInput">
</cfoutput>
</cfform>
DAO Code:
<cfcomponent name="TADAO" displayname="" hint="" output="false">
<cffunction name="read" access="public" returntype="Void" output="false" hint="CRUD method">
<cfargument name="ta" type="ta" required="yes" />
<cfset var qRead = 0 />
<cfquery name="qRead" datasource="#Application.dbsource#" >
SELECT
TAID,
TAHL,
TAIMAGE,
TADISCRIPTION
FROM
T026_TILE_ADS
</cfquery>
<cfif qRead.RecordCount>
<cfset arguments.ta.setTAID(qread.TAID) />
<cfset arguments.ta.setTAHL(qread.TAHL) />
<cfset arguments.ta.setTAImage(qread.TAIMAGE) />
<cfset arguments.ta.setTADiscription(qread.TADISCRIPTION) />
<cfelse>
<cfthrow type="emptyRecordset" errorcode="TADAO.read.emptyRecordset" message="Unable to locate the TILE_AD data record." />
</cfif>
</cffunction>
<cffunction name="update" access="public" returntype="Void" output="false" hint="CRUD method">
<cfargument name="TA" type="TA" required="yes" />
<cfquery name="qUpdate" datasource="#Application.dbsource#">
UPDATE
T026_TILE_ADS
SET
TAID = <cfqueryparam cfsqltype="cf_sql_numeric" value="#arguments.ta.getTAID()#" />,
TAHL = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.ta.getTAHL()#" />,
TAIMAGE = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.ta.getTAImage()#" />,
TADISCRIPTION = <cfqueryparam cfsqltype="cf_sql_text" value="#arguments.ta.getTADiscription()#" />
</cfquery>
</cffunction>
</cfcomponent>
Component code:
<cfcomponent displayname="ta" hint="" output="false">
<cfproperty name="TAID" displayname="" type="numeric" />
<cfproperty name="TAHL" displayname="" type="string" />
<cfproperty name="TAImage" displayname="" type="string" />
<cfproperty name="TADiscription" displayname="" type="string" />
<cffunction name="init" access="public" returnType="TA" output="false" hint="">
<cfset this.TAID = 0 />
<cfset this.TAHL = "headline" />
<cfset this.TAImage = "IMAGE" />
<cfset this.TADiscription = "Discription" />
<cfreturn this />
</cffunction>
<cffunction name="getTAID" access="public" output="false" returntype="numeric">
<cfreturn this.TAID />
</cffunction>
<cffunction name="setTAID" access="public" output="false" returntype="void">
<cfargument name="TAID" type="numeric" required="true" />
<cfset this.TAID = arguments.TAID />
<cfreturn />
</cffunction>
<cffunction name="getTAHL" access="public" output="false" returntype="string">
<cfreturn this.TAHL />
</cffunction>
<cffunction name="setTAHL" access="public" output="false" returntype="void">
<cfargument name="TAHL" type="string" required="true" />
<cfset this.TAHL = arguments.TAHL />
<cfreturn />
</cffunction>
<cffunction name="getTAImage" access="public" output="false" returntype="string">
<cfreturn this.TAImage />
</cffunction>
<cffunction name="setTAImage" access="public" output="false" returntype="void">
<cfargument name="TAImage" type="string" required="true" />
<cfset this.TAImage = arguments.TAImage />
<cfreturn />
</cffunction>
<cffunction name="getTADiscription" access="public" output="false" returntype="string">
<cfreturn this.TADiscription />
</cffunction>
<cffunction name="setTADiscription" access="public" output="false" returntype="void">
<cfargument name="TADiscription" type="string" required="true" />
<cfset this.TADiscription = arguments.TADiscription />
<cfreturn />
</cffunction>
<cffunction name="dump" access="public" output="true" returntype="void">
<cfoutput>
Tile: #this.TAID# <br />
Headline: #this.TAHL# <br />
Image: #this.TAImage# <br />
Description: #this.TADiscription# <br />
</cfoutput>
</cffunction>
TAID as a primary key
TAID is the identifier of each of your tiles, so first of all you need to add it to a WHERE clause in the SQL statements.
UPDATE
T026_TILE_ADS
SET
TAHL = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.ta.getTAHL()#" />,
TAIMAGE = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.ta.getTAImage()#" />,
TADISCRIPTION = <cfqueryparam cfsqltype="cf_sql_text" value="#arguments.ta.getTADiscription()#" />
WHERE
TAID = <cfqueryparam cfsqltype="cf_sql_numeric" value="#arguments.ta.getTAID()#" />
SELECT
TAID,
TAHL,
TAIMAGE,
TADISCRIPTION
FROM
T026_TILE_ADS
WHERE
TAID = <cfqueryparam cfsqltype="cf_sql_numeric" value="#arguments.ta.getTAID()#" />
Calling the update query before the select query
Update: after analysing your database code, the order of definition of cfquerys is not important since they are inside CFFUNCTIONS of the CFCOMPONENT.
Selecting OPTION with Jquery
Finally you will need to select the tile from the submitted form, you can spice your code with Jquery (add inside, at the end of the CFOUTPUT)
<!--- (beginning of CFOUTPUT ...) --->
<input type="Submit" value="Submit" name="Submit" class="formInput">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>
<script>
$(document).ready(function(){
$('SELECT##TAID').val(#variables.ta.TAID#);
});
</script>
</cfoutput>
Edit 26/07
Using <CFSELECT> instead
In the CFOUTPUT the TAID field is a simple HTML SELECT. Since we want to pull records from the DB, try this:
Put before the CFOUTPUT tag:
<cfquery name="qryTiles" datasource="#Application.dbsource#">
SELECT
TAID,
TAHL,
TAIMAGE,
TADISCRIPTION
FROM
T026_TILE_ADS
</cfquery>
And replace:
<select name="TAID" id="TAID">
<option value="0">None</option>
<option value="1">Tile 1</option>
<option value="2">Tile 2</option>
<option value="3">Tile 3</option>
</select>
with:
<cfselect name="TAID"
size=1
multiple="no"
query="qryTiles"
value="TAID"
display="TAID"
queryPosition="below">
<option value="0">None</option>
</cfselect>
I am using ColdFusion 9, and MySQL 5.1. I am trying to align the ColdFusion encrypt/decrypt functions and mySQL AES_ENCRYPT/AES_DECRYPT so I can use them interchangeably depending on the situation. Not having much luck with that.
First I created an AES string with ColdFusion:
<cfset theKey = generateSecretKey("AES") />
<cfoutput>#theKey#</cfoutput>
Example key: 4OFWUiuqFEkGrSRFm8sLlg==
I use this key to encrypt with MySQL. Note, encrypt_test is an existing table, and fld is a varchar column.
INSERT INTO encrypt_test
SET fld = aes_encrypt('the text to encrypt', '4OFWUiuqFEkGrSRFm8sLlg==')
Next I try to decrypt with ColdFusion:
<cfset theKey = "4OFWUiuqFEkGrSRFm8sLlg=="
<cfset theAlgorithm = "AES" />
Then run a cfquery to get the data (Only 1 record in the table),
<cfquery name="testDecrypt">
SELECT fld FROM encrypt_test
</cfquery`
And finally decrypt
<cfoutput>#Decrypt(testDecrypt.fld, theKey, theAlgorithm)#</cfoutput>
This results in a Null. I suspect its a padding issue or some other mismatch, anyone have an idea what I am doing wrong, or how to make this work?
I know this thread is old, but the answer came up on a recent thread. So I am posting it for posterity. As explained in this blog entry, the reason for the difference is:
.. the MySQL algorithm just or’s the bytes of a given passphrase
against the previous bytes if the password is longer than 16 chars and
just leaves them 0 when the password is shorter than 16 chars.
So you need to perform the same manipulations on the key value, before passing it into encrypt/decrypt.
I would stick with just using CF's functions. That way you can add all kinds of layers of security processes, to include things like iterations and multiple keys, to build a custom solution with ease. THe amount of overhead it adds is not much at all for that as well.
Why don't you use ColdFusion's encrypt function instead of MySQL's?
In fact that would be one way to test where the problem might lie : try outputting both the encrypted value from your database and what CF's encrypt function would produce and see if they're identical.
Alternatively just use the aes_decrypt function in your query instead of using ColdFusion's decrypt.
Hmmm, from the docs:
Because AES is a block-level algorithm, padding is used to encode uneven length strings and so the result string length may be calculated using this formula:
16 * (trunc(string_length / 16) + 1)
If AES_DECRYPT() detects invalid data or incorrect padding, it returns NULL.
So assuming CFML doesn't do that padding, you'd have to figure out the reverse of this yourself or something.
I know it's quite an old post but here is what you should do:
Before storing into the DB:
<cfset crypt_fld = #encrypt('the text to encrypt', thekey, 'AES')#>
Then:
INSERT INTO encrypt_test
SET fld = crypt_fld
It worked for me
Use jBCrypt :: bCrypt is the strongest encryption available ... with the assistance of Mark Mandel's Fantastic JavaLoader
implementing jBCrypt is a snap in ColdFusion ...
As far as the password field it really doesn't matter what kind of database you're using ... the field could be varchar(60) or nvarchar(60) if you're dealing with locale support too...
<cfcomponent title="bcrypt (strong; recommended)" hint="I encode passwords using a popular secure password hashing algorithm called bcrypt. I am very slow, but that makes me very secure!" extends="PasswordHash"
alias="bcrypt" seq="9001" workFactor="10">
<cfset variables.loadPaths = [expandPath( "/PATHTOLIBDIR/lib/jbcrypt/jbcrypt-0.3m.jar" )]/>
<cffunction name="init" access="public" output="true" returntype="any" hint="constructor">
<cfset super.init( )/>
<!--- Allow java loader to fail silently: we can report the failure via isAvailable() --->
<cftry>
<cfset variables.oBCryptClass = createJavaClass( "org.mindrot.jbcrypt.BCrypt" )/>
<cfcatch></cfcatch>
</cftry>
<cfreturn this/>
</cffunction>
<cffunction name="isAvailable" hint="Is the hashing agorithm available in this environment?" access="public" returntype="boolean">
<cfreturn structKeyExists( variables, "oBCryptClass" )/>
</cffunction>
<cffunction name="matchesHashFormat" hint="Does the string match the format for this hash?" access="public" returntype="boolean">
<cfargument name="input" type="string" hint="String that may be a password hash" required="true"/>
<cfreturn REFind( "^\$2a\$\d+\$[\./A-Za-z0-9]+$", arguments.input )/>
</cffunction>
<cffunction name="encode" hint="Convert a clear password to its encoded value" access="public" returntype="string">
<cfargument name="password" type="string" hint="Input password" required="true"/>
<cfset var salt = variables.oBCryptClass.gensalt( JavaCast( "int", this.workFactor ) )/>
<cfreturn variables.oBCryptClass.hashpw( arguments.password, salt )/>
</cffunction>
<cffunction name="getHashWorkFactor" hint="Retrieve the work factor from a hashed string" access="public" returntype="numeric">
<cfargument name="hashedPassword" type="string" hint="Previously encoded password string" required="true"/>
<cfset var stMatch = ReFind( "^\$2a\$(\d+)\$([\./A-Za-z0-9]+)$", arguments.hashedPassword, 1, "true" )/>
<cfif stMatch.pos[1] eq 0>
<cfreturn 0>
<cfelse>
<cfreturn mid( arguments.hashedPassword, stMatch.pos[2], stMatch.len[2] )>
</cfif>
</cffunction>
<cffunction name="passwordMatch" hint="Compare a plain password against an encoded string" access="public" returntype="boolean">
<cfargument name="password" type="string" hint="Input password" required="true"/>
<cfargument name="hashedPassword" type="string" hint="Previously encoded password string" required="true"/>
<cfargument name="bCheckHashStrength" type="boolean" default="false" hint="If true, the hash strength of the hashed password must also match those generated by encode()"/>
<cfset var bMatch = variables.oBCryptClass.checkpw( arguments.password, arguments.hashedPassword )/>
<cfif bMatch and bCheckHashStrength>
<!--- Hash matched but we also need to match the bCrypt work factor --->
<cfreturn getHashWorkFactor( arguments.hashedPassword ) eq this.workFactor/>
<cfelse>
<cfreturn bMatch/>
</cfif>
</cffunction>
The PasswordHash.cfc ...
<cfcomponent hint="I am an abstract component for encoding passwords for storage and comparing passwords against previously encoded strings">
<!--- Array of Java class paths required for this component. Leave empty if no special Java libraries are needed. --->
<cfset variables.loadPaths = []/>
<cffunction name="init" access="public" output="true" returntype="any" hint="constructor">
<cfset var stMetadata = getMetadata( this )/>
<cfset var attr = ""/>
<cfloop condition="not structisempty(stMetadata)">
<!--- Get attributes --->
<cfloop collection="#stMetadata#" item="attr">
<cfif issimplevalue( stMetadata[attr] ) and not listcontains( "bindingname,extends,fullname,functions,hint,name,namespace,output,path,porttypename,serviceportname,style,type,wsdlfile", attr ) and not structkeyexists( this, attr )>
<cfset this[attr] = stMetadata[attr]/>
</cfif>
</cfloop>
<!--- Do the same for ancestors --->
<cfif structkeyexists( stMetadata, "extends" )>
<cfset stMetadata = stMetadata.extends/>
<cfelse>
<cfset stMetadata = structnew( )/>
</cfif>
</cfloop>
<cfset stMetadata = getMetadata( this )/>
<!--- If key isn't specified, use the name of the component --->
<cfif not structkeyexists( this, "alias" )>
<cfset this.alias = listlast( stMetadata.name, "." )/>
</cfif>
<!--- If title isn't specified, use the displayname --->
<cfif not structkeyexists( this, "title" )>
<cfset this.title = this.displayname/>
</cfif>
<!--- If seq isn't specified, use 9999 --->
<cfif not structkeyexists( this, "seq" )>
<cfset this.seq = 9999/>
</cfif>
<cfreturn this/>
</cffunction>
<cffunction name="isAvailable" hint="Is the hashing agorithm available in this environment?" access="public" returntype="boolean">
<cfreturn true/>
</cffunction>
<cffunction name="matchesHashFormat" hint="Does the string match the format for this hash?" access="public" returntype="boolean">
<cfargument name="input" type="string" required="true" hint="String that may be an encoding of a password"/>
<cfthrow message="The #this.alias# password encoding needs to implement the matchesHashFormat function"/>
<cfreturn ""/>
</cffunction>
<cffunction name="encode" hint="Convert a clear password to its encoded value" access="public" returntype="string">
<cfargument name="password" type="string" required="true" hint="Input password"/>
<cfthrow message="The #this.alias# password encoding needs to implement the encode function"/>
<cfreturn ""/>
</cffunction>
<cffunction name="passwordMatch" hint="Compare a plain password against an encoded string" access="public" returntype="boolean">
<cfargument name="password" type="string" required="true" hint="Input password"/>
<cfargument name="hashedPassword" type="string" required="true" hint="Previously encoded password string"/>
<cfargument name="bCheckHashStrength" type="string" default="false" hint="If true, the hash strength of the hashed password must also match those generated by encode()"/>
<cfthrow message="The #this.alias# password encoding needs to implement the passwordMatch function"/>
<cfreturn false/>
</cffunction>
<!--- Private Java library helper functions --->
<cffunction access="private" name="getJavaLoader" returntype="any" output="false">
<!--- Lazy-loading the JavaLoader makes it easier for plugins/projects to add custom crypto libraries --->
<cfif not structKeyExists( variables, "loader" )>
<cfset variables.loader = createObject( "component", "PATH.TO.JavaLoader" ).init( variables.loadPaths )/>
</cfif>
<cfreturn variables.loader/>
</cffunction>
<cffunction access="private" name="createJavaClass" returntype="any" output="false" hint="Return a java class from the crypto libraries">
<cfargument name="className" type="string" required="true"/>
<cfreturn getJavaLoader( ).create( arguments.className )/>
</cffunction>
... yada yada ... more code ...