ColdFusion & Ajax: Error Invoking CFC - ajax

I have tried multiple tutorials on this topic from Forta.com and yet run into the same error:
"Error invoking CFC/....(file path)../wgn.cfc: Internal Server Error [Enable
debugging by adding 'cfdebug to your URL parameters to see more info]"
I am working on my local machine and testing as localhost. Running WinXP Pro with sp3. Using Coldfusion's web server.
Both my .cfm and .cfc are in the same folder under the the webroot. In my case:
c:\ColdFusion9\wwwroot\bridges(.cfm and .cfc here)
So, they are in a "bridges" folder under wwwroot.
The code should generate some autosuggest functionality when the user types in the input box. Instead, it just spits back the above error.
This is my cfc named wgn.cfc:
<cfcomponent output="false">
<cfset THIS.dsn="bridges">
<!--- Lookup used for auto suggest --->
<cffunction name="getWGN" access="remote" returntype="array">
<cfargument name="search" type="any" required="false" default="">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(1)>
<!--- Do search --->
<cfquery datasource="#THIS.dsn#" name="data">
SELECT tblIDs.ID
FROM tblIDs
WHERE (tblIDs.IDType = 'xxx') AND (tblIDs.ID Like ('#ARGUMENTS.search#%'));
</cfquery>
<!--- Build result array --->
<cfloop query="data">
<cfset ArrayAppend(result, searchIDs)>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
</cfcomponent>
And this is the relevant part of the form from my .cfm page:
<cfform .....>
<cfinput name="searchIDs" type="text" autosuggest="cfc:wgn.getWGN({cfautosuggestvalue})">
//......more to form, obviously
</cfform>

UPDATE
Solution:
change
<cfset ArrayAppend(result, searchIDs)>
to
<cfset ArrayAppend(result, ID)>

Related

How to verify valid session and session data in ColdFusion .cfc page?

I have been developing single page application that runs on ColdFusion 2016. I already developed login page and now I would like to develop security for each Ajax call that is sent to .cfc page. There is many functions in this file and my main focus is to check if user Session exists and to check if Logged in User has Admin or User access. There is set of functions that should be accessed only with Admin access. The other functions are available for both User and Admin. Here is example of my Session scope that keeps all user info after successfully passed authentication:
<cfset var appStruct = structNew()>
<cfset structInsert(appStruct, "SysAdmin", SystemAdmin, true)>
<cfset structInsert(appStruct, "UserName", UserName, true)>
<cfset structInsert(appStruct, "AccountID", AccountID, true)>
<cfset structInsert(appStruct, "Email", Email, true)>
<cfset SESSION.AccountInfo = appStruct>
Code above is executed once user is logged in, then I have check on the top of my .cfc file:
<cfcomponent>
<cfif structKeyExists(SESSION, "AccountInfo")>
<cfset LoggedAccountID = SESSION.AccountInfo.AccountID>
<cfset isAdmin = SESSION.AccountInfo.SysAdmin EQ 1 ? true : false>
<cfelse>
<cfabort>
</cfif>
/* Here I have many functions that are used by Users and Administrators. */
</cfcomponent>
Then on top of this in each cffunction I always check if AccountInfo session exists and if Admin access is set to true for Administrator functions. Example of one of the functions that is only accessed by Administrators:
<cffunction name="findAccount" access="remote" output="false" returnformat="JSON">
<cfargument name="sessionID" type="string" required="yes" default="fakeSession">
<cfargument name="frmFindaccount_search" type="string" required="yes">
<cfset var fnResults = structNew()>
<cfset var runProcess = true>
<cfif !isAdmin OR !structKeyExists(SESSION, "sessionid") OR (SESSION.sessionid NEQ arguments.sessionID) OR !structKeyExists(VARIABLES, "LoggedAccountID")>
<cfset var fnResults.status = "400">
<cfset var fnResults.message = "Credentials Error!">
<cfreturn fnResults>
<cfabort>
</cfif>
</cffunction>
I'm wondering if all steps above are enough to protect unauthorized users to access any functions/data in my system? Also the other questions is about this block of the code:
<cfif !isAdmin OR !structKeyExists(SESSION, "sessionid") OR (SESSION.sessionid NEQ arguments.sessionID) OR !structKeyExists(VARIABLES, "LoggedAccountID")>
<cfset var fnResults.status = "400">
<cfset var fnResults.message = "Credentials Error!">
<cfreturn fnResults>
<cfabort>
</cfif>
I have this on the top of each cffunction in my .cfc(some functions do not have !isAdmin). I'm wondering if this can be only placed on the top of my .cfc instead of having this in each function? If anyone see any code that can be improved or have any advise please let me know. This is my first individual project and I'm trying to follow standards and prevent security threads.

If user logoff or their session times out how to end coldfusion session?

I have system that I built and for this project I used Ajax with JQuery. On back end I use ColdFusion and session management to handle users session variables. There is two situations (actually three if we consider closing the browser) where users session will end. One situation is if they click Logout then I have function that will clear SESSION scope with all information in it but session is not ended. Also CFID and CFTOKEN are still the same. Here is example of Logout function:
<cffunction name="LogOut" access="remote" output="yes" returnformat="JSON" hint="Used in session timeout handling">
<cfset fnResults = structNew()>
<cfset dt = createODBCDateTime(now())>
<cfif structKeyExists(SESSION,"LoggedIn")>
<cfset temp = structClear(SESSION)>
<cfset fnResults.status = "200">
<cfelse>
<cfset fnResults.status = "400">
<cfset fnResults.message = "Error!">
</cfif>
<cfreturn fnResults>
</cffunction>
Second scenario is once user session timeout. Here is example of that fucntion:
<cffunction name="timeoutSession" access="remote" output="yes" verifyclient="no" securejson="false">
<cfset temp = structClear(SESSION)>
</cffunction>
Both of these functions will clear the session scope but not end the user session. I have used cfdump to check session scope once user logs out and CFID/CFTOKEN remains the same. I'm wondering how session cna be ended once they hit LogOut or timeoutSession function? Also should I rewrite the CFID and CFTOKEN every time user logs in the system? Here is example of my Application.cfc:
<cfcomponent output="false">
<cfset THIS.name = "MyApplication">
<cfset THIS.sessionManagement = true>
<cfset THIS.applicationTimeout = CreateTimeSpan(0, 8, 0, 0)>
<cfset THIS.sessionTimeout = CreateTimeSpan(0, 2, 0, 0)>
<cfset THIS.requestTimeOut = "60">
<cffunction name="onApplicationStart" access="public" returntype="boolean" output="false">
<cfset APPLICATION.appStarted = now()>
<cfset APPLICATION.title = "My Application">
<cfset APPLICATION.functions = CreateObject("component","udfs").init()>
<cfset APPLICATION.sessionMinutes = 30>
<cfreturn true>
</cffunction>
<!--- Runs when your session starts --->
<cffunction name="OnSessionStart" access="public" returntype="void" output="false">
<!--- Clear the session. --->
<cfset StructClear( SESSION ) />
<!--- Set loggedin flag to false. --->
<cfset SESSION.loggedin = false>
<cfreturn />
</cffunction>
<!--- Run before the request is processed. --->
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfargument name="thePage" type="string" required="true">
<cfset REQUEST.appCode = 'SPA'>
<cfset REQUEST.appName = 'Single Page Application'>
<cfset var page = listLast(arguments.thePage,"/")>
<!---<cfset onApplicationStart()>--->
<cfif !listFindNoCase("Login.cfm,Authentication.cfc",page)>
<cfif !structKeyExists(SESSION, "loggedin") OR SESSION.loggedin EQ false>
<cflocation url="Login.cfm" addToken="false">
</cfif>
</cfif>
<cfreturn true>
</cffunction>
</cfcomponent>
I'm not sure if session can be ended in Application.cfc or I have to do that in my cffunctions. Same for CFID and CFTOKEN what is the best place to set new values if user logs in again? If aynone have experience with this please let me know. I'm trying to prevent user to use the same session and raise level of security in my SPA.

Cross-domain scripting ColdFusion issue

I have a silly question but I am a newbie. I am trying to make a cross-domain request (via ajax) using extjs4 library. As server side language I am using ColdFusion. The whole code that I have written so far is working on same domain. I need to separate static files (javascript, css, and html) placing them on Apache server and the dynamic content (cfm, CFC) placing them over Tomcat (openbd). So the front end scripts (on Apache->javascript mainly) making requests on Tomcat in order to fetch content. This definitely is cross-domain requests.
A code block that I am using in order to pull data (CFC) is:
<cfcomponent output="false">
<cfprocessingdirective pageencoding="utf-8">
<cfset setEncoding("URL", "utf-8")>
<cffunction name="getContent" access="remote" returnFormat="JSON" output="false" >
<cfargument name="start" default="0"/>
<cfargument name="limit" default="1000"/>
<cfargument name="id" default="0" required="false" type="numeric">
<cfargument name="model" default="" required="false" type="any">
<cfset var arrNomoi = ArrayNew(1)>
<cfset var stcReturn = "">
<!--- When going back to base state, ExtJS will make the function call with start set to 0. If this is the case
we set it to 1 --->
<cfset Arguments.start = Arguments.start + 1>
<cfif arguments.model EQ 'n_2664_1998'>
<cfquery name="getNomoi" datasource="ktimatologio">
SELECT CONCAT_WS('_',id,model) AS id, id AS 'id1', CONCAT_WS(' ',title,fek,date) AS 'title', description, body, model
FROM n_2664_1998
WHERE id = #arguments.id#
ORDER BY id ASC
</cfquery>
<cfelseif arguments.model EQ 'n_2308_1995'>
<cfquery name="getNomoi" datasource="ktimatologio">
SELECT CONCAT_WS('_',id,model) AS id, id AS 'id1', CONCAT_WS(' ',title,fek,date) AS 'title', description, body, model
FROM n_2308_1995
WHERE id = #arguments.id#
ORDER BY id ASC
</cfquery>
<cfelseif arguments.model EQ 'n_3889_2010'>
<cfquery name="getNomoi" datasource="ktimatologio">
SELECT CONCAT_WS('_',id,model) AS id, id AS 'id1', CONCAT_WS(' ',title,fek,date) AS 'title', description, body, model
FROM n_3889_2010
WHERE id = #arguments.id#
ORDER BY id ASC
</cfquery>
</cfif>
<cfset arrNomoi = convertQueryToExtJS(getNomoi,Arguments.start,Arguments.limit)>
<cfset stcReturn = {"data"=arrNomoi,dataset=#getNomoi.RecordCount#}>
<cfreturn stcReturn>
</cffunction>
<cffunction name="convertQueryToExtJS" access="public" hint="Convert Query to JSON usable by ExtJS Grid" returntype="array">
<cfargument name="qryData" type="query" required="true" hint="Query to convert">
<cfargument name="intStart" type="numeric" required="true" hint="Start of Result Set">
<cfargument name="intLimit" type="numeric" required="true" hint="How many records to return">
<!--- For the Array --->
<cfset var i = 1>
<cfset var end = ((Arguments.intStart) + arguments.intLimit)-1>
<cfset var arrNomoi = ArrayNew(1)>
<cfloop query="qryData" startrow="#Arguments.intStart#" endrow="#end#">
<cfset stcNomoi = StructNew()>
<cfset stcNomoi['id'] = #qryData.id#>
<cfset stcNomoi['id1'] = #qryData.id1#>
<cfset stcNomoi['title'] = #qryData.title#>
<!---<cfset stcNomoi['fek'] = #qryData.fek#>
<cfset stcNomoi['date'] = #qryData.date#>--->
<cfset stcNomoi['description'] = #qryData.description#>
<cfset stcNomoi['body'] = #qryData.body#>
<cfset stcNomoi['model'] = #qryData.model#>
<cfset arrNomoi[i] = stcNomoi>
<cfset i = i + 1>
</cfloop>
<cfreturn arrNomoi>
</cffunction>
</cfcomponent>
The question is: how do I wrap the above function into a variable (named “callback”) and post it on client?
I have seen a similar code block on PHP but I don’t understand. Below is the PHP code block:
<?php
$callback = $_REQUEST['callback'];
// Create the output object.
$output = array('id' => 1, 'url' => 'loianegroner.com');
//start output
if ($callback) {
header('Content-Type: text/javascript');
echo $callback . '([' . json_encode($output) . ']);';
} else {
header('Content-Type: application/x-json');
echo json_encode($output);
}
?>
With respect,
Tom
Greece
Take a look at these two files:
The index.cfm is the handler where you submit an AJAX request to and it will return a JSON result
http://websvn.openbd.org/websvn/filedetails.php?repname=OpenBD&path=%2Ftrunk%2Fwebapp%2Fmanual%2Fapi%2Findex.cfm
The index.cfm interacts with this CFC to fetch the information and return the result.
http://websvn.openbd.org/websvn/filedetails.php?repname=OpenBD&path=%2Ftrunk%2Fwebapp%2Fmanual%2Fapi%2Findex.cfc
When I was writing this API wrapper I had issues with returnFormat=JSON

How can I clear the ColdFusion template cache as part of a deployment?

I ran into an issue this morning after deploying of some files to a ColdFusion website/application.
I updated an existing CFC with some new code. The CFC has an init() method that returns the instantiated Object:
Original MyObject.cfc:
<cfscript>
VARIABLES.MyParam = "";
</cfscript>
<cffunction name="init" returntype="MyObject" output="false">
<cfargument name="MyParam" type="String" required="true" />
<cfscript>
VARIABLES.MyParam = ARGUMENTS.MyParam;
return THIS;
</cfscript>
</cffunction>
New MyObject.cfc:
<cfscript>
VARIABLES.MyParam = "";
</cfscript>
<cffunction name="init" returntype="MyObject" output="false">
<cfargument name="MyParam" type="String" required="true" />
<cfscript>
setMyParam(ARGUMENTS.MyParam);
return THIS;
</cfscript>
</cffunction>
<cffunction name="setMyParam" output="false" returntype="Void">
<cfargument name="MyParam" type="String" required="true" />
<cfset VARIABLES.MyParam = Trim(ARGUMENTS.MyParam) />
</cffunction>
<cffunction name="getMyParam" output="false" returntype="String">
<cfreturn VARIABLES.MyParam />
</cffunction>
Any time an Object that extended this CFC called init(), it was throwing an exception:
"The value returned from the init function is not of type MyObject."
This issue did not occur in any of the other environments in which this change was deployed - only in Production.
The only thing that fixed it was clearing the template cache in ColdFusion Administrator.
So, I'm either looking for a way to prevent this from happening in the future and/or a way to automatically clear the template cache when I deploy files.
FYI, I currently deploy files using Tortoise SVN.
In your init() (or more preferably, in another reload-style method), programmatically call the Admin API's clearTrustedCache() method:
<cfscript>
// Login is always required (if the administrator password
// is enabled in the ColdFusion Administrator).
// This example uses two lines of code.
adminObj = createObject("component","cfide.adminapi.administrator");
adminObj.login("admin");
// Instantiate the runtime object.
myObj = createObject("component","cfide.adminapi.runtime");
// clear cache
myObj.clearTrustedCache();
// Stop and restart trusted cache. However, only the clearTrustedCache function needs to be called.
myObj.setCacheProperty("TrustedCache", 0);
myObj.setCacheProperty("TrustedCache", 1);
</cfscript>
This functionality's been in place as far back as CF7 (Source). Note that you will need the CF Admin password for this.
I would also recommend clearing the component cache, if you have that option enabled in your admin:
myObj.clearComponentCache();

Coldfusion 8 - Best practice for DAO AJAX access?

I'm fairly new to using OO concepts such as DAO's, Gateways etc and I'm trying to work out the best way to implement an AJAX accessible CFC whilst trying not to repeat lot's of code.
I have the following DAO which holds CRUD methods for my DB table and takes the application DSN as an argument in it's constructor:
<cfcomponent name="property_imageDAO" displayname="property_imageDAO" output="false" hint="">
<!--- pseudo constructor --->
<cfscript>
variables.dsn = application.dsn;
</cfscript>
<!--- constructor --->
<cffunction name="init" access="public" output="false" returntype="any"
hint="Constructor for this CFC">
<!--- take DSN as argument --->
<cfargument name="dsn" type="string" required="true" hint="The datasource name" />
<!--- put dsn in variables scope so we can use it throughout the CFC --->
<cfset variables.dsn = arguments.dsn />
<!--- return this CFC --->
<cfreturn this />
</cffunction>
<!--- CRUD methods (create, read, update, delete) --->
<!--- CREATE: inserts a new property_image into the database --->
<cffunction name="createRecord" access="remote" output="true"
hint="Creates a new property_image record and returns a struct containing a boolean (success) indicating the success or
failure of the operation, an id (id), and a string (message) containing a message">
<!--- take property_image bean as argument --->
<cfargument name="property_image" type="any" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<cfset var qInsertproperty_image = 0 />
<!--- defaults --->
<cfset results.success = true />
<cfset results.message = "The record was inserted successfully." />
<!--- insert the property_image --->
<cftry>
<cfquery name="qInsertproperty_image" datasource="#variables.dsn#">
INSERT INTO property_image (
name,
alt
)
VALUES (
<cfqueryparam value="#arguments.property_image.getname()#" cfsqltype="cf_sql_varchar" />,
<cfqueryparam value="#arguments.property_image.getalt()#" cfsqltype="cf_sql_varchar" />
)
</cfquery>
<cfcatch type="database">
<cfset results.success = false />
<cfset results.message = "Inserting the record failed. The error details if available are as follows: " & CFCATCH.Detail />
</cfcatch>
</cftry>
<!--- return the struct --->
<cfreturn StructCopy(results) />
</cffunction>
Should I add functionality to this DAO to make it AJAX accessible or should I create another DAO specifically for remote access?
Thanks
I think there's probably as many variations of a solution to this as there will be people to suggest one, but here's one take on it.
I'd not openthe DAO up to REMOTE access, I'd leave that as PACKAGE (and only be accessed by some business object in the same package). I'd also have some sort of facade sitting in front of that lot which handles the remote calls, as well as things like validating whether the call coming in remotely IS ALLOWED to be making the call. You don't want just anyone sticking stuff in your DB! The facade should deal with the auth side of things, then if all OK pass the call to the business object which then uses the DAO to access the DB.
I would not have that try/catch stuff in your DAO either. The best way to notify calling code that something went wrong is for an exception to be thrown. Then the calling code can decide what to do with it (whether to deal with it some way, ignore it, or re-bubble it to the sitewide error handling).
I suggest looking at ColdSpring and its ability to create remote proxies and its ability to secure them using AOP. This is a great way to expose only certain parts of a CFC to remote access and to control by whom they are accessed.
Both topics are covered in the ColdSpring Quick Start guide: http://www.coldspringframework.org/coldspring/examples/quickstart/
I also did a presentation on how to do this. You can see a recording here: http://textiles.online.ncsu.edu/online/Viewer/?peid=a4227aeb1ad84fa89eeb3817f075af5b1d

Resources