cffunction httpMethod attribute validation? - coldfusion

Is there an administrative option that controls whether the httpMethod attribute of an access='remote' cffunction is validated / required?
I have two server instances, dev and prod, both CF11; on the dev box I can successfully call a remote function defined like:
<cffunction name="foo" access="remote" returnFormat="json">
...
</cffunction>
But on the prod box it returns 500 invalid endpoint definition: no httpMethod declared.
Easy to fix—just place the attribute on it—but I'd like to bring my setups closer together in terms of configuration.

Related

Enctype issue when I try to upload a file with a Coldfusion webservice

Can't get to work a Coldfusion webservice that uploads a file in an external server.
My application runs in "Server A" and the file repository (external server) is in "Server B"
The template (inicio.cfm) which contains the form with the <cfinput type="file"> to select the client's file to be uploaded, is stored in "Server A". This template performs more tasks than just show the upload form. It performs calculations, queries, etc. outside the form's code block. Also, the action page of this form is the template itself (because of my application's needed tasks).
The first code line of my form definition is (inside inicio.cfm):
<cfform method="post" name="AltaConvocatoria" enctype="multipart/form-data">
Which demonstrate that I'm using the right enctype definition.
In the other hand, stored in "Server B" is my Coldfusion component or webservice (alta_ga.cfc) which only task is to upload the file selected by user in "inicio.cfm" form and rename it.
Here's alta_ga.cfc code:
<cfcomponent>
<cffunction access="remote" returntype="void" name="cargaAnuncio">
<cfargument name="destinoAnuncio" required="yes" type="string">
<cfargument name="PrefijoNvoNombre" required="yes" type="string">
<cffile action="upload"
fileField="str_ArchivoAnuncio"
destination="#destinoAnuncio#"
nameconflict="Overwrite">
<cfset NvoNomAnuncio = #PrefijoNvoNombre# & #Right(cffile.ClientFile, 5)#>
<cfset viejoNombre1 = #destinoAnuncio# & #cffile.ClientFile#>
<cffile
action = "rename"
destination = "#NvoNomAnuncio#"
source = "#viejoNombre1#">
</cffunction>
</cfcomponent>
For that pupose, I invoke the webservice from the form's action code block in inicio.cfm with this:
<cfinvoke webservice="http://192.168.208.128/podi/mgmt/alta_ga.cfc?wsdl" method="cargaAnuncio" >
<cfinvokeargument name="destinoAnuncio" value="#form.destinoAnuncio#" />
<cfinvokeargument name="PrefijoNvoNombre" value="#form.PrefijoNvoNombre#" />
</cfinvoke>
When I try to load a file using my form's template inicio.cfm I get this message:
Cannot perform web service invocation cargaAnuncio.
The fault returned when invoking the web service operation is:
'' podi.mgmt.PodiMgmtAlta_gaCfcCFCInvocationExceptionException: coldfusion.tagext.io.FileUtils$CFFileNonMultipartException : Invalid content type: application/soap+xml; charset=UTF-8; action="urn:cargaAnuncio".The files upload action requires forms to use enctype="multipart/form-data".]
All the arguments and variables that I'm using are correct, because I tested the webservice as a local component (stored in Server A and uploading the file in the same server) and worked fine. Here's the code of the succesful test (invoked as a component instead of a webservice):
<cfinvoke component="alta_ga" method="cargaAnuncio" destinoAnuncio="#form.destinoAnuncio#" PrefijoNvoNombre="#form.PrefijoNvoNombre#">
¿What could be wrong?
There's a lack of documentation about this functionality. Adobe's user guide doesn't explain this functionality in depht. Ben Forta's books... same. Or I couldn't find the information.
Thanks in advance.
When a form is posted to a CFML server, the posted file is saved in a temporary directory before any of your code runs. All <cffile action="upload"> does is to copy a file from that temporary directory to the location you want it to be. Your remote server ServerB has no idea about any file posted on ServerA, so <cffile action="upload"> will not help you.
The action is misleading. It's not upload-ing anything. It's just copying from a predetermined temp directory. The web server handles the uploading before the CF server is even involved.
You will likely need to <cffile action="upload"> on ServerA to a specific place, and then it needs to post that file to your web service on ServerB. Then ServerB should be able to use <cffile action="upload"> to transfer it from the upload temp directory to wherever you need it to be. That said I have never tried this when posting to a web service.
Alternatively you could just post the file directly to ServerB in the first place, to save needing ServerA to be an intermediary. This might not be possible, of course.

Exempting Coldfusion page from authentication

I need to post from a non-secure CF page to a secure CF page. I don't want to have to go through and implement the user authentication on the page sending the values because its a rather cumbersome process due to the way this legacy site was setup and secondly because the page sending the values is acting as a service between two unrelated order management systems as opposed to a user.
Right now, when I try to post to, the response result is a redirect to the login of the homepage. Is there a way to make an exception for a posting or receiving page from forcing user authentication?
I'm using <cfhttp> to post the values to post page which has a series of <cfparam>'s that I'm passing the values to. Once I pass those values into the post page is when the post page triggers a redirect to the home page because the post page is an internal page in the order management system and is displayed as a client logs in and a session is created for them.
Since you did not provide any code, here is a guess what it might look like and how you could add an exception for specific requests:
<cffunction name="onRequestStart" access="public" output="false" returnType="boolean">
<cfargument name="targetPage" type="string" required="true">
<!--- treat initialized SESSION or matching request token (rtoken) as successful authentication --->
<cfset LOCAL.isAuthenticated = (
isDefined("SESSION.userID")
or
( structKeyExists(FORM, "rtoken") and (FORM["rtoken"] eq "some-secret-only-you-know") )
)>
<cfif LOCAL.isAuthenticated>
<!--- do something... --->
<!--- not authenticated --->
<cfelse>
<!--- redirect to login --->
<cflocation url="login.cfm" statusCode="303" addToken="false">
</cfif>
</cffunction>
Now you could simple add the key-value-pair rtoken=some-secret-only-you-know (i.e. <input type="hidden" name="rtoken" value="some-secret-only-you-know" />) to your POST to bypass the session based authentification.
Disclaimer: Only use this method if the POST parameters (form fields) are not public/editable by the user.
Feel free to provide actual context so I can assist in a more concrete way.
I have written a couple of apps with similar, but not identical requirements. Here is how I handled those requirements in the last one I wrote. All this code is the Application.cfc file in the methods specified.
In onApplicationStart:
application.securityNotNeededPages =
"somePage.cfm,someOtherPage.cfm,someMorePages.cfm";
In onRequestStart
var ThisPage = listlast(cgi.PATH_INFO, "/");
...
if (ListFindNoCase(application.securityNotNeededPages, ThisPage) is false) {
security related code
}
else {
code for when the page does not to be secured
}

REST Web Service 404 Not found - ColdFusion 11

I created a new function in an existing REST cfc but when I am trying to call it I receive a 404 Error where the rest of the functions are working. All of the functions are following the same structure as the one I am about to link further down without any issues.
I saw another post like mine but I didn't find any answers in it. This is the link for the other post here
<cfcomponent restpath="student" rest="true">
<cffunction name="npssummary" access="remote" output="false" returntype="any" httpmethod="get" restpath="npssummary" produces="application/json">
<cfquery name="nps_summary" datasource="dpsigweb2">
select top 10 * from contact
</cfquery>
<cfreturn serializeJSON(nps_summary,"struct")>
</cffunction>
</cfcomponent>
And this is how I am calling it
<cfhttp url="http://dev.example.com/rest/IIT/student/npssummary" method="get">
<cfset results = "#cfhttp#">
<cfdump var="#results#">
When I am trying to call the function directly in the browser I receive the expected result.
Also, I am using this function to reset the REST services each time I make a change to my component it seems to be working as expected so far.
<cftry>
<cfset restInitApplication("Z:\Sites\testSites\API\","IIT")>
<cfcatch type="any">
<cfdump var="#cfcatch#">
</cfcatch>
</cftry>
This is a browser limitation:
Microsoft Internet Explorer has a maximum uniform resource locator (URL) length of 2,083 characters. Internet Explorer also has a maximum path length of 2,048 characters. This limit applies to both POST request and GET request URLs.
If you are using the GET method, you are limited to a maximum of 2,048 characters, minus the number of characters in the actual path.
However, the POST method is not limited by the size of the URL for submitting name/value pairs. These pairs are transferred in the header and not in the URL.
RFC 2616, "Hypertext Transfer Protocol -- HTTP/1.1," does not specify any requirement for URL length.

Setting session variables with JavaScript in ColdFusion

I have a website with multiple tabs. Each tab runs a separate report based on a set of filters that take their values from session variables.
How things work now:
While the user is inside a report tab they can open a filter menu to select the options that they need to run their report (doctor names, locations, date, etc) and then they can hit the run button to get their report. When the user clicks "run" the form is saving the variables inside the session where they are available to run other reports without having to click "run" or define them again and again.
What I am trying to do:
Instead of having only a "run" button inside the form I need an "Apply" button that will set the session variables from the form without running the current report. This way the user can pre-define their variables without being forced to run a report they don't need.
I tried using ajax that calls a function outside my application which is setting up variables based on the user's selection.
My challenge is to get those variables back from the function in some format where I could use them in updating the current session variables.
This is a sample of my code:
The Apply button:
Apply
My Ajax Function:
function setSession(){
var formData = $('form').serialize();
$.ajax({
url:'/mod_example/components/exampleCFCs/xUtility.cfc?method=setSessionVariables',
data: formData
});
};
And part of my function:
<cfcomponent output="no">
<cffunction name="setSessionVariables" access="remote" returntype="any">
<cfargument name="docid" type="string" required="no">
<cfif isDefined('docid')>
<cfset session.doctorids = docid>
</cfif>
<cfif isDefined('docid')>
<cfreturn session.doctorids>
<cfelse>
<cfreturn 0>
</cfif>
</cffunction>
</cfcomponent>
What I need is to get the value of session.doctorids to be able to update my session variables with the new value.
It sounds like you have this utility cfc in a shared directory and you are calling it directly. As you've noticed, the problem with that is that you end up with multiple sessions. You can get around this issue be setting up a Facade cfc within your application and make your ajax calls to that cfc.
If you only want to expose the setSessionVariables then you could use this cfc:
<cfcomponent output="no">
<cffunction name="setSessionVariables" access="remote" returntype="any">
<cfset var xUtility = createObject('component','mod_example.components.exampleCFCs.xUtility')>
<cfreturn xUtility.setSessionVariables(argumentCollection=ARGUMENTS)>
</cffunction>
</cfcomponent>
If you want to expose all methods of the utility cfc, then you can extend it:
<cfcomponent output="no" extends="mod_example.components.exampleCFCs.xUtility">
</cfcomponent>
This would allow you to call methods on the utility cfc while maintaining a single session scope (per user of course).
EDIT:
Been a while since i've worked in wheels...but i remember not liking AJAX in the wheels framework. If you create a new subfolder and call it 'remoting' and put the facade in there, and drop an application.cfc in there that looks like this:
<cfcomponent >
<cfset this.name = 'whatever_your_wheels_app_name_is'>
<cfset this.SessionManagement=true>
</cfcomponent>
You should be able to use that facade and this application.cfc will piggyback on the existing application with the same name. The problem with this approach would be if the application times out, and a remote call is the first request to the application, then the wheels application scope might not get set up properly.
It would be best if you could extend the root application.cfc and just override the onRequestStart method so that the framework will ignore the request. To do that you would need to make a mapping in the cfadmin to the root of your project and use this for your remoting/application.cfc
<cfcomponent extends="mappingName.Application">
<cffunction name="onRequestStart">
<cfargument name="requestname" required="true" />
<cfset structDelete(this,'onRequest')>
<cfset structDelete(this,'onRequestEnd')>
<cfset structDelete(VARIABLES,'onRequest')>
<cfset structDelete(VARIABLES,'onRequestEnd')>
<cfreturn true>
</cffunction>
</cfcomponent>
The way that wheels uses `cfinclude' all over the place, you may need to look at this post about extending the appliciation: http://techblog.troyweb.com/index.php/2011/09/cfwheels-workarounds-numero-uno-application-proxy/
There are some wheels plugins (http://cfwheels.org/docs/1-1/chapter/wheels-ajax-and-you) that allow you to use the controller actions / views / routes via ajax so you could look into those also.

Requesting an Application.cfc Element of 'THIS'

If this has been asked before I apologize but I wasn't able to get a solid enough understanding without some assistance ....
I have a Local ColdFusion9 App that I use for development and testing running on WinXP SP3 with Apache 2 ...
What I'm having a problem understanding how to call request elements passed from the Application's 'THIS' scope to the onRequestStart() method.
For instance, My Application.cfc has the following in 'THIS' ...
<!--- MY 'THIS' Statements in Application.cfc --->
<cfscript>
/* define custom coldfusion mappings. Keys are mapping names, values are full paths */
this.mappings = structNew();
this.mappings['/tags'] = ExpandPath('/cfdev/tags');
</cfscript>
Further into my Application.cfc I have the following in the onRequestStart() function ...
<!--- Run before the request is processed --->
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfargument name="thePage" type="string" required="true">
<cfinclude template="#arguments.thePage#">
<!--- Lot's of onRequest statements and then ... --->
<cfset request.mappings = #THIS.mappings#>
<cfreturn true>
</cffunction>
Now ... Supposing I have a page where I call <cfdump var="#request.mappings#"> ...
I get an error stating 'Element MAPPINGS is undefined in REQUEST.' ...
However, (here's where my confusion begins) ... If I call <cfdump var="#request#"> Two structures are returned ... The first containing a key for "cfdumpinited" with a value of 'False' and the second with the structKey 'mappings' which contains another struct with a key of '/tags' and a value of 'C:\vir_dir\CFDEV\tags' as one would expect ...
If someone could please explain why it is that request succeeds yet request.mappings fails as I'm a bit in the woods here ... ;-)
Follow the logic through:
a request is made
onRequestStart runs
you explicitly include the template requested
youTHEN set request.mappings = this.mappings
onrequeststart finishes
the template requested executes
So if you have this in your requested template:
<cfdump var="#request.mappings#>
Then when you include that file at step 3... request.mappings doesn't exist yet (as they are created in step 4).
However when you change it to:
<cfdump var="#request#>
Then there's no error condition, so your CFINCLUDE runs fine, but DOESN'T OUTPUT ANYTHING (because you have output="false" on the function definition.
The dump you are seeing is the one coming from CF executing the requested template (step 6), by which time request.mappings exists.
I think you are confusing onRequestStart() with onRequest(), to be honest. It's in onRequest() that one might explicitly include the requested template, because onRequest() runs INSTEAD of CF running the requested template automatically. One would not generally include the requested template in onRequestStart() because it'll end up running twice.
Make sense?
I'm late to the party here, and may be missing a key element, but why not do this:
application.mappings = structNew()
instead of
this.mappings = structNew()
that way, you only set them once (on application start), you have access to them across your whole application, and you don't have the extra overhead of setting the request scope on every page hit.