Cflocation bug / new feature in ColdFusion 9 - URL appending twice - coldfusion

When a user logs in and is redirected to a secured page, the url is getting appended twice like a list. This in turn causes a 404.
(example: http://uwf.edu/something.cfm,http://uwf.edu/something.cfm)
Currently, the site has a custom login tag which I am unable to edit as I do not have control over it. (It's just a custom cf tag that allows people to login at the university.)
I have to do additional processing after this tag to verify that they are eligible to login on this particular site. Once they have been verified, they are re-directed to another page with cflocation.
<custom login tag>
<cfinvoke component="#application.path#cfc/security" method = "constructSession" returnvariable = "status">
.. params excluded..
</cfinvoke>
<cfif status eq 1>
<cflocation url="#someurl_invalid#" addtoken="no" />
<cfelse>
<cflocation url="#someurl#" addtoken="no" />
</cfif>
The custom login tag refreshed the current page already, but I obviously do not want that and thus had used the above method to re-direct. This worked in ColdFusion 8.
I read this article: http://www.bennadel.com/blog/2050-Changes-In-CFLocation-OnRequestEnd-Behavior-In-ColdFusion-9-s-Application-cfc.htm
The article gave me some insight as to what is going on...but I am unsure how to fix the issue.
Does anyone have any solutions?

Since you don't have control over the custom tag, you'll have to work around the issue instead of fixing it.
I would recommend changing the code:
<cfif status eq 1>
<cflocation url="#ListFirst(someurl_invalid)#" addtoken="no" />
<cfelse>
<cflocation url="#ListFirst(someurl)#" addtoken="no" />
</cfif>
It's not pretty but will work whether the URLs are lists or not.

Related

Variable session passes from page to page coldFusion

CFBuilder admin storage
15cdb5dcb6.jpg
Application.cfm
34ed7586e1.jpg
Login.cfm
<cfif not isDefined('FORM.submitButton')>
<cfform name="loginForm" method="post" action="#CGI.SCRIPT_NAME#">
Login:
<cfinput type="text" name="login" required="yes">
Password:
<cfinput type="password" name="password" required="yes">
<br>
<cfinput type="submit" name='submitButton' value="Sign">
<br>
<cfinput type="button" name='registerButton' value="Register">
</cfform>
<cfelse>
<cfquery name='getUser' datasource="dbfortest">
SELECT * FROM usertable WHERE login="#FORM.login#" ;
</cfquery>
<cfif getUser.RecordCount NEQ 0>
<cfif FORM.password eq getUser.password>
<cflock scope="Session" timeout="60" type="exclusive" >
<cfset Session.loggedIn = "yes">
<cfset Session.user = "#FORM.login#">
</cflock>
<cfoutput>#StructKeyList(Session)#</cfoutput>
<cfelse>
Your pass isn't correct.
</cfif>
<cfelse>
There is no user with this name.
</cfif>
</cfif>
part of page when i want to use login including.
<cfif Session.loggedIn eq "no">
<cfinclude template="login.cfm">
</cfif>
<cfif structKeyExists(session, "user")>
<cfoutput>Welcome, #Session.user#.</cfoutput>
</cfif>
<cfoutput>#StructKeyList(Session)#</cfoutput>
Hello everyone, please help me understand these sessions' behavior.
The whole problem consists in attempting to pass variables from one page to another.
So after login i don't see the session.user in session struct.
How can i pass this?
Have already tried different browsers.
#Aquitaine has given you some good information. I just wanted to also point out that another part of your problem is likely that you have set a 10 second life span for your sessions. That's probably not long enough.
In the Application.cfm example that you posted you have this line:
sessiontimeout="#createTimespan(0,0,0,10)#"
The arguments for the CreateTimeSpan function are as follows:
createTimespan(days, hours, minutes, seconds)
As such you are assigning a 10 second lifespan for sessions. Perhaps you meant to set 10 minutes instead of 10 seconds.
To figure out what's going on with the session variables, try putting in some debug code right after your cfset session statements to make sure that they're happening. Maybe <cfdump var="#session#">.
You do not need to cflock your session scope (and have not needed to since CFMX). See Adam Cameron's 2013 post on when to lock scopes
If your debug code runs and you see the session variables, but then they're gone on the next page, that may be an issue with your session storage (which is a different part of cfadmin) or else whatever front-end webserver you're using. Try <cfdump var="#session#"> in onRequestStart in Application.cfc and make sure that JSESSIONID is the same on every request. (or try disabling J2EE session variables in CFADMIN and see if the same problem persists with CFID/CFTOKEN).
If your debug code doesn't run, then you should be seeing one of your error conditions.
For ease-of-reading, be consistent in your casing when refering to scopes, e.g. session not Session. While this kind of thing may not matter functionally, it can get you into trouble with portability when referencing paths or components.
Some other issues:
If you are going to use a boolean value for loggedIn then use a boolean value: true or false or 1 or 0 or (if you must) yes or no but not "yes" which is a string; instead of being able to do if (session.loggedIn) if you will have to do if (session.loggedIn == 'yes') and nobody will be happy.
If this is meant to be working, production site code, at a minimum you need to be using cfqueryparam as you do not ever want to pass unescaped user input directly to a database query.
You might also head over to the CFML slack at cfml.slack.com and ask on #cfml-beginners for some pointers on writing login forms.

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
}

Login System in ColdFusion 9

I am working with a very old login system that my company used before on a website that used frames.
Before, when someone tried a wrong user/pass combination the frame would load a simple cfinclude file with the login form and an error message on top of it.
Now I am using a form in a popup window that calls the application.cfc but instead of getting the error message back on my popup window the page load the cfinclude file from the application component to a new page.
So I need a few things to happen for this application. First, I need the initial popup window to stay up and the page should not submit if the combination of user/pass is wrong, and finally I need the error message to appear somewhere on the popup.
If anyone did something like this before I would really appreciate your feedback.
This is a partial of my code:
Login Form:
<!--- loginErrMsg display - to tell why login is denied --->
<cfif isdefined("loginErrMsg")><span style="color:red">#loginErrMsg#</span><br /></cfif>
<form name="LoginForm" id="LoginForm" action="<cfif test is false>https://secure.example.com</cfif>#loginFormAction#" method="post" target="_top">
</cfoutput>
<input type="hidden" name="loginPost" value="true">
<p>
Login below to take advantage of the great services we offer:
</p>
E-mail:<input name="j_username" class="loginform" type="text" size="30" maxlength="50" id="j_username">
Password: <input name="j_password" type="password" size="30" maxlength="16" class="loginform">
<br />
<input type="submit" name="btn" value="Submit" class="bluebuttonnormal">
</form>
Application.cfc Code:
<cflogin applicationtoken="swmadmin">
<cfif NOT IsDefined("cflogin")>
<cfinclude template="login.cfm">
<cfabort>
<cfelse>
<cfquery name="userlookup" datasource="#ds#">
SELECT clientadminID, roles, isFaxOnly, acctEnabled FROM clientadmin
WHERE
username=<cfqueryparam value="#cflogin.name#" CFSQLTYPE="CF_SQL_VARCHAR" maxlength="50">
and password=<cfqueryparam value="#cflogin.password#" CFSQLTYPE="CF_SQL_VARCHAR" maxlength="16">
</cfquery>
<cfif userlookup.recordcount eq 0>
<cfset loginErrMsg = "Invalid login.">
<cfinclude template="login.cfm">
<cfabort>
</cflogin>
I am working with a very old login system that my company used before
on a website that used frames.
If this is a new website, don't use it. Login forms are a dime a dozen and can be done in your sleep. Start fresh and do it right.
So I need a few things to happen for this application. First, I need
the initial popup window to stay up and the page should not submit if
the combination of user/pass is wrong, and finally I need the error
message to appear somewhere on the popup.
You're going to want to use an AJAX solution here, either write your own or use a good library like jQuery. Once you check the login values you can use jQuery or simple javascript to unhide or update the innerHTML of an empty element to display your error message.
<cflogin ...>
...
</cflogin>
CFLogin makes me sad. Another one of ColdFusion's tags meant to simplify something commonly done that doesn't really help much and sacrifices flexibility. You can get far more control over your application without it. instead of CFLogin, try something like this pseudo code
<cfcomponent>
<cffunction name = "onRequest" ...>
<cfargument name="targetPage" type="String" required = "true" />
<cfif !structKeyExists(session, "roles") and !findNoCase("loginHandler.cfm",cgi.script_name)>
<!--- notice I prevent the redirect on the form handler, otherwise the ajax will get redirected to the login.cfm page --->
<cfinclude template = "login.cfm">
<cfelse>
<cfinclude template = "#arguments.targetPage#">
</cfif>
</cffunction>
</cfcomponent>
Your login.cfm would then contain your form but your button would fire something like jQuery.post() to "loginHandler.cfm", then depending on the result of the login, your callback function may use jQuery.html() to display the error or window.location.replace / window.location.href if the login was successful. Of course, in the event of a successful login, your ColdFusion page would have to create their session variables and do whatever else you want it to do before sending the result back to your AJAX call.

Is a CFLogin replacement necessary or am I missing something?

I'm seriously considering moving away from CF8 cflogin because it is tied to the server that spawned the login. In a load balanced environment you're stuck with sticky sessions if you don't do a custom implementation.
Does anyone have any source that mimics CFLogin that writes to and is managed from the client scope? Maybe even a design that matches up well with a rename replace on isuserin[any]role.
What should I be thinking about when I consider writing a replacement implementation for CFLogin?
Here is a basic non cflogin approach using variables stored in the CLIENT scope. We use a similar approach for non-sticky sessions across our server cluster behind our load balancer.
This code should live in Application.cfc -> onRequestStart() method:
<!--- handle login *post* --->
<cfif structKeyExists(FORM, "pageaction") and FORM.pageAction eq "adminlogin">
<!--- attempt to log user in --->
<cfif loginSuccessful>
<!--- Set client variables for session management --->
<cfset CLIENT.lastHit = now() />
<cfset CLIENT.loggedIn = 1 />
<!--- redirect to home page --->
<cfelse>
<!--- redirect to login page with message --->
</cfif>
<!--- all other requests, except for the login page --->
<cfelseif structKeyExists(CLIENT, "lasthit") and structKeyExists(COOKIE, "cfid") and structKeyExists(CLIENT, "cfid") and listLast(CGI.SCRIPT_NAME, "/") neq "login.cfm">
<!--- Check for timeout --->
<cfif (datediff("n", CLIENT.lastHit, now()) lte 10) and (CLIENT.loggedIn is 1) and (CLIENT.cfid is COOKIE.cfid)>
<!--- record last hit --->
<cfset CLIENT.lastHit = now() />
<cfelse>
<!--- timeout! redirect to login page --->
<cflocation URL="http://mydomain/login.cfm" addtoken="false" />
</cfif>
</cfif>
There is user role stuff, but I hope this helps as a starting point.
I customized the CF Login Wizard through Dreamweaver to be portable and to use a db table for authentication and role management. Because of this,I can use it either as a single-user login, or multiple account logins. I never have used cflogin and haven't needed to. I just drop the files into the directory, customize the login credentials, and that is it. Works perfect every time.

How do I redirect based on referral in ColdFusion

I have a coldfusion web site I need to change. Have no idea or experience with this environment (I do know ASP.NET). All I need to do is to write a condition based on the referral value (the URL) of the page, and redirect to another page in some cases.
Can anyone give me an example of the syntax that would perform this?
All of the other examples would work...also if you're looking to redirect based on a referral from an external site, you may want to check CGI.HTTP_REFERER. Check out the CGI scope for several other options.
<cfif reFindNoCase('[myRegex]',cgi.http_referer)>
<cflocation url="my_new_url">
</cfif>
...my example uses a regex search (reFind() or reFindNoCase()) to check the referring URL...but you could also check it as a list with / as a delimiter (using listContainsNoCase()) depending on what you're looking for.
Lets assume your the URL variable you are basing this on is called goOn (http://yoursite.com?goOn=yes) then the following code would work:
<cfif structKeyExists(url, "goOn") AND url.goOn eq "yes">
<cflocation url="the_new_url" addtoken="false">
</cfif>
Nothing will happen after the cflocation.
There is a CGI variable scope in ColdFusion that holds information on the incoming request. Try the following:
<cfif CGI.SCRIPT_NAME EQ 'index.cfm'>
<cflocation url="where you want it to redirect" />
</cfif>
To see what else is available within the CGI scope, check out the following:
http://livedocs.adobe.com/coldfusion/8/htmldocs/Expressions_8.html#2679705
Haven't done coldfusion in a little while but:
<cfif some_condition_based_on_your_url>
<cflocation url="http://where_your_referrals_go">
</cfif>
<!--- continue processing for non-redirects --->
A dynamic version.
<cfif isdefined("somecondition")>
<cfset urlDestination = "someurl">
<cfelseif isdefined("somecondition")>
<cfset urlDestination = "someurl">
.
.
.
<cfelse>
<cfset urlDestination = "someurl">
</cfif>
<cflocation url = urlDestination>