What's the best way to logout with ColdFusion? - coldfusion

cflogout does not seem to clear the CFID and CFTOKEN values. Is cflogout only used with cflogin?
In this instance, I am not using cflogin. I am setting session variables because of the issues with cflogin. It worked before CF11 Update 7, however, CF11U7 seemed to resolve the cflogin double issue. Anyone else?
Is this all that I need? If so, it's not working.
<cfset structDelete(session, "CFTOKEN")>
<cfset structDelete(session, "CFID")>
What is the best way to logout using ColdFusion?

<cfset sessionInvalidate()>
This function will not only clear the session scope, but also invalidate the CFID/CFToken.

<cfscript>
StructClear(Session)
</cfscript>
This will delete ALL session variables.
(1)
<cfscript>
StructDelete(Session.MySessionVariable)
</cfscript>
(2)
<cfscript>
StructDelete(Session, "MySessionStructure")
</cfscript>
Will delete a specific variable (1) or structure(2) in the session scope.

<cfset StructDelete(SESSION,"user")>
Here user is the structure that i made during the login

Related

How to set and check session in Coldfusion 11?

I am very new to ColdFusion. I am building a very basic system in which I now need to set a session variable, in case of successful login. But I don't know how to set session in ColdFusion or how to check it on application pages.
I searched for solution but could not find satisfactory solution. I need some example in which a session is set in case of login, and destroyed on logout.
I've read about Application.cfm, but where this file is located?
This could be a simple question and may be repeating in some ways.
Thanks.
First of all you should probably use:
Application.cfc
You can either use:
OnSessionStart
In which case you don't need to lock your session variables, as Coldfusion takes care of this.
If you set your session variable outside of this method, you may need to lock the variable like:
Before login:
<cfif NOT StructKeyExists(session,"authenticated")>
<cflock scope="session" timeout="30" type="exclusive">
<cfset session.authenticated = false />
</cflock>
</cfif>
New account creation:
When a user logs in, remember to use something like BCrypt() to hash the password & store in DB. Don't encrypt passwords as these can be unencrypted and this can create a potential security loophole.
https://github.com/tonyjunkes/BCryptCFC
<cfset salt = BCrypt.genSalt()>
<cfset hash = BCrypt.hashString("password", salt)>
Login validation:
After a user has logged in, use BCrypt() to check whether the clear text password matches the password hash in your DB:
<cfset BCrypt.checkString("password", hash)>
Also check that the 'username' [e-mail] matches...
If BCrypt() validation is successful, then set your 'session' variable:
<cflock scope="session" timeout="30" type="exclusive">
<cfset session.authenticated = true />
</cflock>
This is a very basic implementation of how the login should work, but it gives you an idea of how to get started.
Another tip, is that, if you are locking your 'session' variables, then instead of having to continually use:
<cflock>
When reading from the session, it would be advisable to convert your 'sesssion' variable into the 'variables' scope, like:
<cflock scope="session" timeout="10" type="readOnly">
<cfset variables.lckauthenticated = session.authenticated />
</cflock>
You can then use:
variables.lckauthenticated
Outside of the <cflock> tag.
Now, there is some debate as to whether you need to lock 'session' variables, but my rule, and one that is recommended in Adobe's Official Documentation, is to lock 'session' variables, if you are reading & writing to them outside of onSessionStart.
Then, when your user logs out, just set:
<cflock scope="session" timeout="30" type="exclusive">
<cfset session.authenticated = false />
</cflock>
You can then use this flag, to determine what is displayed to your user.
I usually set several 'session' variables during a successful login, like:
session.userid
session.roleid
There are other things like 'session' rotation, which help to safeguard your session, but these are more advanced topics for another post...

How to ensure a cfobject variable is local to a function?

Assuming the following code in a regular .cfm file, not a CFC:
<cffunction name="myFunction">
<cfobject type="java" action="create" class="path.to.my.java.class" name="myJavaVariable">
</cffunction>
What is going to be the scope of the created object? I'm getting the feeling it's not limited to the function scope. If so, how to make it local? Would the following work?
<cfset var myJavaVariable = "">
<cfobject ... name="myJavaVariable">
EDIT:
I forgot to say I was under Coldfusion MX 7, as it does prevent some useful solutions found on the web from working.
I did find a workaround where one creates a fake local scope (<cfset var local = StructNew()>). It does work well even for such tags since you can put the return variable as local.someVar.
I'd question why you're using <cfobject> at all, rather than the more obvious simple assignment:
var myJavaVariable = createObject("java", "path.to.my.java.class");
Isn't that more natural code anyhow?

onRequestStart user authorisation does not work correctly coldfusion

Im my Application.cfc I am trying to use the onRequestStart() function to protect my pages as such:
<cffunction name="onRequestStart" access="public" returntype="boolean">
<cfargument type="String" name="TargetPage" required="true"/>
<!--- Define which pages don't need protection --->
<cfset APPLICATION.AllowedPages = "/index.cfm, /register.cfm">
<!--- Create an instance of the page-protect.cfc --->
<cfset APPLICATION.PageProtect = CreateObject("component", "page-protect")>
<!--- check if the current page is an allowed page --->
<cfif #ListFindNoCase(APPLICATION.AllowedPages, ARGUMENTS.TargetPage)# EQ 0>
<!--- if its not an allowed page, then protect it --->
<cfscript>
APPLICATION.PageProtect.PageProtectBasic(argumentcollection = session);
</cfscript>
</cfif>
<cfreturn true>
</cffunction>
This code works (kind of as you will see later). Page-protect.cfc is very simple and does this:
<cfcomponent displayname="page-protect" output="false">
<cffunction name="PageProtectBasic" output="no">
<cfif NOT structKeyExists (SESSION, 'Auth')>
<cflocation url="/index.cfm" addtoken="no">
</cfif>
</cffunction>
</cfcomponent>
So if the Auth structure within the SESSION scope does not exist, then this user is not logged in and should be taken back the homepage. A logout method in a different file deletes the Auth structure from SESSION and also clears the SESSION scope (if definitely does this I have tested it).
The onRequestStart() page protection works initially but I have noticed that when I press the back button on my browser it will show the previous page that I just logged out of. This should be a protected page and not display but I guess its a browser cache so not a problem. However the problem is that if I click on a link in this page it SHOULD not allow it and send the user back to home page (because the SESSION.Auth structure does not exist and SESSION has been cleared). But it does not send the user back to the homepage anymore, it just shows a ColdFusion error page stating that "Element AUTH.{element_name} is undefined in SESSION".
So for some reason its not going back to the homepage despite the user not being logged in, and instead is trying to load the protected page and then falling over because a variable within the SESSION.AUTH structure does not exist. I simply don't understand what I'm doing wrong. Please help!
While James Mohler provides some very helpful pointers on how to improve your code in general the issue you are having is not related to that.
The reason that users can see these pages on hitting back is because they are cached in the browser. This is the browser trying to be helpful and not requesting data from the server that it has already seen. The browser being a good internet citizen will do what it is told though. So you need to return the correct HTTP headers to tell it that you don't want it to cache them. E.g.
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
So to do this in CF
<cfheader name="Cache-Control" value="no-cache, no-store, max-age=0, must-revalidate">
<cfheader name="Pragma" value="no-cache">
If you add the above to the pages that are concerning you, the problem will go away.
Simon
Sorry, this won't fit into a comment box
I think what you are trying to do is something like this:
onApplicationStart() {
APPLICATION.PageProtect = CreateObject("component", "page-protect")>
}
onSessionStart() {
session.auth = false; // it is easier to work with if it always exists
}
onRequestStart() {
if (ListFindNoCase(APPLICATION.AllowedPages, ARGUMENTS.TargetPage) EQ 0)
// if its not an allowed page, then protect it
APPLICATION.PageProtect.PageProtectBasic(argumentcollection = session);
}
}
Possibly not related to your problem but this line might have a couple of issues.
<cfset APPLICATION.AllowedPages = "/index.cfm, /register.cfm">
Issue number 1 is the leading slashes. Unless arguments.TargetPage has those slashes, and they might, you are not going to get the expected behaviour.
Issue number 2 is the space between your two list items. Leading spaces are part of the list item which could lead to unexpected behaviour on register.cfm.

Friendly Url in format 'mydomain.com/username' without Mod Rewrite?

I would like to know if there's an easier way other than Mod Rewrite (using the fusebox framework or directly in Coldfusion) to convert a url as follows:
from:
http://www.somedomain.com/salmahayek
or
http://localhost/someApp/salmahayek
to:
http://www.somedomain.com/index.cfm?action=profile.view&name=salmahayek
or
http://localhost/someApp/index.cfm?action=profile.view&name=salmahayek
My app is an existing Fusebox 5.5 application.
I just need to add that the url above is not static, i.e. "salmahayek" could be any name.
Any help would be greatly appreciated
Thanks
You could potentially use the "classic" way of doing it (not sure if Fusebox will interfere), using a 404 handler, something like this should do the trick:
Set up a 404 hander on your server, e.g. in .htaccess:
ErrorDocument 404 /404handler.cfm
set up 404handler.cfm to wrap around the framework, e.g.:
<cfset variables.checksok = false>
<!--- do some checks - example --->
<cfif cgi.REDIRECT_URL EQ 'salmahayek'>
<cfset variables.checksok = true>
</cfif>
<cfif variables.checksok EQ true>
<cfheader statuscode="200" statustext="OK">
<cfset url.action = "profile.view">
<cfset url.name = cgi.REDIRECT_URL>
<cfinclude template="index.cfm">
</cfif>
(not tested but should work)
I've doing some like this in one my apps currently, albeit in PHP:
http://localhost/index.cfm/profile.view/salmahayek/
<cfset urlArgs=listToArray(CGI.PATH_INFO, "/") />
<cfset action=urlArgs[1] />
<cfset name=urlArgs[2] />
This works perfectly, but you have to put up with the "index.cfm" if you don't want to rewrite.
I'm not sure about anyone else, but I don't understand why Mod Rewrite would be difficult, unless you are on IIS. A rewrite rule would simply have to be something like:
^(login|register)/([^/\.]+) index.cfm?action=profile.$1&step=$2 [L]
^([^/\.]+)/?$ index.cfm?action=profile.view&name=$1
I put in some extra examples to check if the user is actually trying to get to the registration or login page and what step they are on there.
i've actually done this in past using the Application.cfc's onMissingTemplate() method. you can either do some regexs against the arguments.targetpage that gets passed in or do a lookup in a database. either way you would do a cflocation to the correct page afterward. just remember to pass over any url parameters also.
one thing that i've never tried out and often wondered though is if this could be handled in the onRequestStart() method instead? the biggest problem i have with using onMissingTemplate() is that you're doing a cflocation which is a completely new request and you can't pass through form variables. yes i know you could probably use GetPageContext().Forward( strUrl ) instead, but you're still going threw the entire request lifecycle in for the original request. by doing this in onRequestStart() you would avoid this.
anyone want to test this out?

Is onApplicationStart is good Idea in ColdFusion?

I have to use a Variable(Query Resultset) in ColdFusion, which will get the results from Other Application DB, and stores in Coldfusion Application.
The main idea is that I need to call the other Application DB only at Server startup time and cache the results in local. And I need to read the variable in other pages in my Application. I won't overwrite that variable in any page.
On googling I found that 'onApplicationStart' is useful to assign the variables at Application Startup time.
Is using the onApplicationStart fine or is there any other way? We can assign a variable at startup time(one time).
If onApplicationStart is fine: how to use? Maybe any link where it is explained clearly is helpful.
Well, it depends. How often will this query data be updated? If it really is unchanging, then onApplicationStart() is a fine place to put it. However, if it will change every so often, you can just tell Coldfusion to cache the query for a certain period of time, then you don't need to mess with onApplicationStart(), but rather when you call the query it will return the cached result automatically (within your specified time period).
Regardless, I would write a custom function to retrieve the data. Then it will be trivial to call it from onApplicationStart() or elsewhere.
Startup.cfc: (Named whatever you like)
<!--- Replace the datasource name with your db name --->
<cffunction name="getStartupQuery" hint="Returns a query recordset for startup">
<cfargument name="datasource" required="no" type="string" default="OtherAppDB">
<!--- Init the query variable --->
<cfset var result = queryNew("id")>
<!-- Get the query dataset --->
<cfquery name="result" datasource="#arguments.datasource#">
YOUR QUERY HERE
</cfquery>
<cfreturn result>
</cffunction>
Application.cfc: (Just the important parts)
<cffunction name="onApplicationStart">
<!--- init the startup.cfc, then retrieve the data
and save it to the application scope. Remember the component name must match
your component above --->
<cfset var startup = createObject("component", "startup")>
<cfset application.varFromOtherDB = startup.getStartupQuery()>
<cfreturn true>
</cffunction>
Now, you should be able to access this variable from any CFM or CFC in your application using:
<cfset myNewVar = application.varFromOtherDB>
or
#application.varFromOtherDB#
IF you use the onApplicationStart() method, I highly recommend implementing a method to reinit the application. For an example, see this other discussion.