I have a CFC file with remote access enabled, I am using this to perform various admin tasks such as adding / removing image records from a database. The calls to this CFC are made through AJAX via custom JavaScript on my admin pages. I placed the CFC in what I thought was a secured directory but having had some issues with images disappearing of their own accord and I have since found it is not secure at all.
I would like to secure the CFC. There is already a session based security CFC that I use for the admin pages, and the protect method of that gets called each time one of the admin pages is requested, this redirects a user if the authentication fails. Can I use something this on my CFC? IF so, what is the best way to implement it? If not, how should I implement security on it?
Below is my example CFC:
<cfcomponent
name="test"
displayname="test"
output="false"
hint="test"
>
<!--- pseudo constructor --->
<cfscript>
variables.propertyImageDAO = CreateObject("component","cfcs.dataobjects.property_imageDAO").init(APPLICATION.dsn);
variables.propertyImageGateway = CreateObject("component","cfcs.dataobjects.property_imageGateway").init(APPLICATION.dsn);
</cfscript>
<!--- constructor --->
<cffunction name="init" access="public" output="false" returntype="any"
hint="Constructor for this CFC">
<!--- 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"
>
<cfargument name="name" type="any" required="false" default="" />
<cfargument name="alt" type="any" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- create property bean --->
<cfscript>
var propertyImageBean = CreateObject("component","cfcs.beans.property_image").init(
'',
arguments.name,
arguments.alt
);
results = propertyImageDAO.createRecord(propertyImageBean);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- READ: reads a property_image from the database and populates the property_image object --->
<cffunction name="readRecord" access="remote" output="true" returntype="void"
hint="Reads property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="id" type="numeric" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- create property bean --->
<cfscript>
propertyImageBean = CreateObject("component","cfcs.beans.property_image");
propertyImageBean.setid(arguments.id);
propertyImageDAO.readRecord(propertyImageBean);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(propertyImageBean)#</cfoutput>
</cffunction>
<!--- DELETE: reads a property_image from the database and populates the property_image object --->
<cffunction name="deleteRecord" access="remote" output="true" returntype="void"
hint="Reads property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="id" type="numeric" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- create property bean --->
<cfscript>
results = propertyImageDAO.deleteRecordById(arguments.id);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- DELETERECORDS: deletes a property_image from the database --->
<cffunction name="deleteRecords" access="remote" output="true" returntype="void"
hint="Deletes property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="imageIdList" type="string" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- delete DB records --->
<cfscript>
results = propertyImageDAO.deleteRecordsByIdList(arguments.imageIdList);
</cfscript>
<!--- delete files --->
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- DELETERECORDS: reads a property_image from the database and populates the property_image object --->
<cffunction name="deleteRecordById" access="remote" output="true" returntype="void"
hint="Deletes property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="id" type="numeric" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- delete DB records --->
<cfscript>
results = propertyImageDAO.deleteRecordById(arguments.id);
</cfscript>
<!--- delete files --->
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- DELETERECORDSBYIDLIST: reads a property_image from the database and populates the property_image object --->
<cffunction name="deleteRecordsByIdList" access="remote" output="true" returntype="void"
hint="Deletes property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="imageIdList" type="string" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- delete DB records --->
<cfscript>
results = propertyImageDAO.deleteRecordsByIdList(arguments.imageIdList);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<cffunction name="deleteImagesByNameList" access="remote" output="true" returntype="void"
hint="Deletes property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="imageNameList" type="string" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- delete DB records --->
<cfscript>
results = propertyImageDAO.deleteImagesByNameList(arguments.imageNameList);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- READ: reads a property_image from the database and populates the property_image object --->
<cffunction name="getByIdList" access="remote" output="true" returntype="void"
hint="Reads property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="imageIdList" type="string" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- create property bean --->
<cfscript>
qGetByIdList = propertyImageGateway.getByIdList(arguments.imageIdList);
</cfscript>
<!--- convert into JSON friendly format --->
<cfif qGetByIdList.recordCount GT 0>
<cfset images = ArrayNew(1)>
<cfloop query="qGetByIdList" startRow="1" endRow="#qGetByIdList.recordCount#">
<cfscript>
// create image struct and assign values
image = StructNew();
image.id = id;
image.name = name;
image.alt = alt;
// append to JSON response
ArrayAppend(images,image);
</cfscript>
</cfloop>
<cfset results.images = images>
</cfif>
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
<!--- READ: reads a property_image from the database and populates the property_image object --->
<cffunction name="updateRecord" access="remote" output="true" returntype="void"
hint="Reads property_image data from the database and returns a JSON">
<!--- take property_image bean as argument --->
<cfargument name="id" type="numeric" required="true" />
<cfargument name="name" type="any" required="true" />
<cfargument name="alt" type="any" required="true" />
<!--- initialize variables --->
<cfset var results = StructNew() />
<!--- create property bean --->
<cfscript>
propertyImageBean = CreateObject("component","cfcs.beans.property_image").init(
arguments.id,
arguments.name,
arguments.alt
);
results = propertyImageDAO.updateRecord(propertyImageBean);
</cfscript>
<!--- return the struct --->
<cfoutput>#SerializeJSON(results)#</cfoutput>
</cffunction>
To enforce your authentication logic, you should wrap all remote CFC calls in Application.cfc with this logic.
Unfortunately you're using CF8, so you can't use the onCFCRequest method of Application.cfc to easily wrap all your remote requests. But you can do the same thing in onRequestStart by checking if the target page ends in '.cfc'.
<cffunction name="onRequestStart">
<cfargment name="targetPage">
<cfif right(targetPage, 4) eq '.cfc'>
<!--- Perform authentication check --->
<cfif not loggedIn>
<!--- Return "unauthorized" to the client --->
<cfheader statuscode="401">
<cfabort>
</cfif>
</cfif>
</cffunction>
Then, in your Ajax fail handler, check for a 401 status code and display a message to the user indicating the need for logging in.
Why don't you just use a session token every time you make ajax call.
Place your authentication logic (validate session) into a remote facade, or if you are using any MVC framework, put the authentication logic onto the controller layer.
Return the appropriate HTTP status code (e.g. 403) if session validation fails so the frontend code can react appropriately.
Related
I'm working on my Application.cfc page for my SPA. So far I was able to create simple test and there is one problem that I notice. Flag that I set when user is loggedin always indicates to true. Here is my Application.cf page example:
<cffunction name="onApplicationStart" access="public" returntype="boolean" output="false">
<cfset APPLICATION.appStarted = now()>
<cfset APPLICATION.appCode = 'SPA'>
<cfset APPLICATION.title = "SPA Application">
<cfset APPLICATION.errorEmails = "myemail#gmail.com">
<cfset APPLICATION.CryptWord = randomSequence()>
<cfset APPLICATION.AppStarted = true>
<cfreturn true>
</cffunction>
<!--- Runs when your session starts --->
<cffunction name="OnSessionStart" access="public" returntype="void" output="false">
<!--- Clear the session. --->
<cfset StructClear( SESSION ) />
<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 var page = listLast(arguments.thePage,"/")>
<cfif !listFindNoCase("Login.cfm,Login.cfc",page)>
<cfif !structKeyExists(session, "loggedin") OR session.loggedin EQ false>
<cflocation url="Login.cfm" addToken="false">
</cfif>
</cfif>
<cfreturn true>
</cffunction>
As you can see above I first set some application variables in onApplicationStart() the onSessionStart() first I clear SESSION structure then set flag to false. Here is example of my Login.cfc:
<cfcomponent>
<cffunction name="checkLogin" access="remote" output="false" returnType="boolean">
<cfargument name="username" type="string" required="true">
<cfargument name="password" type="string" required="true">
<cfif arguments.username EQ "test" AND arguments.password EQ "test">
<cfset SESSION.loggedin = true>
<cfreturn true>
<cfelse>
<cfreturn false>
</cfif>
</cffunction>
</cfcomponent>
Then I use cfdump to check my SESSION variable on my Login.cfm (only has HTML form elements and JQuery function to submit the form). Doesn't matter if checkLogin function returns true or false value in loggedin variable is still equal to true. If anyone can see where I'm doing something wrong in my code please let me know. Thank you!
I am populating a cfselect box using the bind attribute. I'd like to be able to make my selection from the two drop downs and then populate a CFTEXTAREA based on that selection. I thought I could just issue a similar bind attribute command but the textarea outputs:
[object Object]
CF8 documentation says that this is possible? What am I missing?
Form Code:
<cfselect name="descriptionDD" value="description" bind="cfc:cfcs.menudata.getData()" bindonload="true" />
<cftextarea name="detailDD" value="service_detail" bind="cfc:cfcs.menudata.getDetail({descriptionDD})" bindonload="true" />
CFC Code:
<cfcomponent>
<!---First Select Box --->
<cffunction name="getData" access="remote" returntype="query">
<!--- Function to get data from datasource --->
<cfquery name="data" datasource="ezpay">
select description
from service_descriptions
order by description
</cfquery>
<!--- Return results --->
<cfreturn data>
</cffunction>
<!---Second Select Box --->
<cffunction name="getDetail" access="remote" returnType="query">
<cfargument name="description" type="any" required="true">
<cfquery name="dataDetail" datasource="ezpay">
SELECT service_detail
from service_descriptions
WHERE description = '#ARGUMENTS.description#'
ORDER BY ID ASC
</cfquery>
<cfreturn dataDetail>
</cffunction>
</cfcomponent>
but the textarea outputs: [object Object]
That usually means you are returning a complex object where a simple string is expected instead. [object Object] is essentially the string representation of the complex object (in your case a query).
TextArea's are designed to display a single value. So your bind function should return a string, not a query object.
<cffunction name="getDetail" access="remote" returnType="string">
<cfargument name="description" type="any" required="true">
<!--- localize function variables --->
<cfset var dataDetail = "">
<cfquery name="dataDetail" datasource="ezpay">
SELECT service_detail
FROM service_descriptions
<!--- adjust cfsqltype if needed --->
WHERE description = <cfqueryparam valu="#ARGUMENTS.description#" cfsqltype="cf_sql_varchar">
ORDER BY ID ASC
</cfquery>
<cfreturn dataDetail.service_detail>
</cffunction>
I was wondering if there is any strings centralization in ColdFusion similar to Android strings.xml file.
So, that my code remains intact if i want to do any changes in string
ColdFusion is a programming language, not a user interface framework.
There is nothing like Android's string resource management built into ColdFusion, but it would be very easy to implement that yourself.
resources/strings.xml:
<!-- keep as a structure with unique element names -->
<strings>
<heading>This is a test.</heading>
<greetings>
<hello>Hello World!</hello>
<bye>Goodbye World!</bye>
</greetings>
</strings>
ColdFusion utility function (for example in a CFC component util.cfc):
<cffunction name="ReadResouceXml" returntype="struct" access="public" output="no">
<cfargument name="path" type="string" required="yes">
<!--- internal use argument --->
<cfargument name="xmlDoc" type="xml" required="no">
<cfset var xmlElem = "">
<cfset var output = StructNew()>
<!--- read XML file from disk --->
<cfif not StructKeyExists(arguments, "xmlDoc")>
<cffile action="read" file="#ExpandPath(path)#" variable="xmlDoc" charset="UTF-8">
<cfset xmlDoc = XmlParse(xmlDoc).XmlRoot>
</cfif>
<!--- recursively convert XML to a CF struct --->
<cfloop index="i" from="1" to="#ArrayLen(xmlDoc.XmlChildren)#">
<cfset xmlElem = xmlDoc.XmlChildren[i]>
<cfif ArrayLen(xmlElem.XmlChildren) gt 0>
<cfset output[xmlElem.XmlName] = ReadResouceXml("", xmlElem)>
<cfelse>
<cfset output[xmlElem.XmlName] = xmlElem.XmlText>
</cfif>
</cfloop>
<cfreturn output>
</cffunction>
Coldfusion usage:
<cfobject type="component" name="util" component="util">
<cfset strings = util.ReadResouceXml("resources/strings.xml")>
<cfoutput>
<h1>#strings.heading#</h1>
#strings.greetings.hello# - #strings.greetings.bye#
</cfoutput>
I am trying to convert some pages from my app to use cfc's, and one page uses a stored procedure to retrieve a couple sets of data.
Now when I access the results, they act just like a if I used a <cfquery> tag, and all of the functionality that gives. So now I am trying to use this same stored procedure in a cfc that I am building, and I would like to be able access the results in the same manner, and there in lies my problem. I'm not sure how to return multiple queries from the function, without creating an array, which I have started. By the way, the function is incomplete. I was just trying to get something to work. In the below setup I get an array of query objects, but I feel there is a better way to do it.
Here is the <cffuntion>:
<cffunction name="getProfileData"
access="public"
output="false"
returntype="string">
<cfargument name="cusip" type="string" required="true">
<cfargument name="report_date" type="date" required="true">
<cfset var errorMessage = "everything is good">
<cftry>
<cfstoredproc datasource="#dsn#" procedure="prc_asset_profile_retrieve">
<cfprocparam type="in" cfsqltype="cf_sql_varchar" value="#cusip#" dbvarname="#cusip">
<cfprocparam type="in" cfsqltype="cf_sql_varchar" value="#report_date#" dbvarname="#reportDate">
<cfprocresult name="profile_head" resultset="1">
<cfprocresult name="attribution" resultset="2">
<cfprocresult name="characteristics" resultset="3">
<cfprocresult name="exposure" resultset="4">
<cfprocresult name="weights" resultset="5">
<cfprocresult name="holdings" resultset="6">
</cfstoredproc>
<cfset var profileArray = []>
<cfset #ArrayAppend(profileArray,profile_head)#>
<cfcatch type="any">
<cfset errorMessage = "something happened">
</cfcatch>
</cftry>
<cfreturn profileArray>
</cffunction>
When I output some test data, it matches up
<cfset count = fund_profile.getProfileData("#cusip#","#report_date#")>
<cfdump var="#count[1]#">
<cfoutput>
From cfc (##count[1].recordCount##): #count[1].recordCount#<br>
From stored proc (##profile_head.recordCount##): #profile_head.recordCount#
</cfoutput>
I get:
From cfc (#count[1].recordCount#): 1
From stored proc (#profile_head.recordCount#): 1
But the second way looks so much cleaner.
-----------------------------WORKING SOLUTION------------------------------
So after working with the answer from #leigh, I came up with this.
Here is the full cfc:
<cfcomponent displayname="Fund Profile" hint="This is the cfc that will do the processing of all fund profile information" output="false">
<cfproperty name = "result1"> <!--- PROFILE HEAD --->
<cfproperty name = "result2"> <!--- ATTRIBUTION --->
<cfproperty name = "result3"> <!--- CHARACTERISTICS --->
<cfproperty name = "result4"> <!--- EXPOSURE --->
<cfproperty name = "result5"> <!--- WEIGHTS --->
<cfproperty name = "result6"> <!--- HOLDINGS --->
<cffunction name="init"
displayname="init"
hint="This will initialize the object"
access="public"
output="false"
returnType="Any">
<cfargument name="dsn" type="string" required="true" />
<cfargument name="cusip" type="string" required="true" />
<cfargument name="report_date" type="date" required="true" />
<cfset variables.dsn = #arguments.dsn#>
<cfset variables.cusip = #arguments.cusip#>
<cfset variables.report_date = #arguments.report_date#>
<cfscript>
getProfiledata(cusip,report_date);
</cfscript>
<cfreturn this>
</cffunction>
<cffunction name="getProfileData"
access="private"
output="false"
returntype="void">
<cfargument name="cusip" type="string" required="true">
<cfargument name="report_date" type="date" required="true">
<cfstoredproc datasource="#dsn#" procedure="prc_asset_profile_retrieve">
<!--- STORED PROCEDURE HASN'T CHANGED. SEE ABOVE FOR CODE --->
</cfstoredproc>
<cfscript>
setProfilehead(profile_head);
setAttribution(attribution);
setCharacteristics(characteristics);
setExposure(exposure);
setWeights(weights);
setHoldings(holdings);
</cfscript>
<cfreturn>
</cffunction>
<!--- NOT GOING TO INCLUDE ALL SETTERS AND GETTERS, --->
<!--- BECAUSE THEY ARE ALL THE SAME OTHER THAN THE NAMES --->
<cffunction name="setProfileHead" access="private">
<cfargument name="ProfileHead">
<cfset variables.result1 = arguments.ProfileHead>
</cffunction>
<cffunction name="getProfileHead" access="public" returntype="query">
<cfreturn variables.result1>
</cffunction>
</cfcomponent>
Here is the code from the calling page:
<cfset fund_profile = CreateObject("component", "CFCs.fund_profile").init("#dsn#","#cusip#","#report_date#")>
<cfset profile_head = fund_profile.getProfileHead()>
Sorry for all the code, but I wanted to make the code available. So does anyone see any problems with what I came up with?
A function can only return a single value. If you wish to return multiple values, you will need to use some type of complex object (an array, structure, ...) If arrays are not intuitive enough, you could place the queries in a structure and return that instead. Then the calling page could access the queries by name, rather than index.
(Side note, be sure to properly var scope/localize all function variables.)
<cfset var data = {}>
...
<!--- store query results in structure --->
<cfset data.profile_head = profile_head>
<cfset data.attribution = attribution>
...
<cfset data.holdings = holdings>
<!--- return structure --->
<cfreturn data>
I would create other methods in the CFC that would each be responsible for returning a result from the stored proc. In the main method , call setters
setProfileHead(profilehead:profileHead)
<cffunction name=ProfileHead>
<cfarguments name=ProfileHead />
<cfset variables.profilehead = arguments.profilehead>
</cffunction>
Then...
<cffunction name=GetProfileHead>
<cfreturn variables.profileHead />
</cffuction>
Is there a recommended (and preferably free) way in ColdFusion to access a remote file that is protected by NTLM authentication? The cfhttp tag appears to only support Basic authentication.
This CFX Tag - CFX_HTTP5 - should do what you need. It does cost $50, but perhaps it's worth the cost? Seems like a small price to pay.
Here is some code I found in:
http://www.bpurcell.org/downloads/presentations/securing_cfapps_examples.zip
There are also examples for ldap, webservices, and more.. I'll paste 2 files here so you can have an idea, code looks like it should still work.
<cfapplication name="example2" sessionmanagement="Yes" loginStorage="Session">
<!-- Application.cfm -->
<!-- CFMX will check for authentication with each page request. -->
<cfset Request.myDomain="allaire">
<cfif isdefined("url.logout")>
<CFLOGOUT>
</cfif>
<cflogin>
<cfif not IsDefined("cflogin")>
<cfinclude template="loginform.cfm">
<cfabort>
<cfelse>
<!--Invoke NTSecurity CFC -->
<cfinvoke component = "NTSecurity" method = "authenticateAndGetGroups"
returnVariable = "userRoles" domain = "#Request.myDomain#"
userid = "#cflogin.name#" passwd = "#cflogin.password#">
<cfif userRoles NEQ "">
<cfloginuser name = "#cflogin.name#" password = "#cflogin.password#" roles="#stripSpacesfromList(userRoles)#">
<cfset session.displayroles=stripSpacesfromList(userRoles)><!--- for displaying roles only --->
<cfelse>
<cfset loginmessage="Invalid Login">
<cfinclude template="loginform.cfm">
<cfabort>
</cfif>
</cfif>
</cflogin>
<!-- strips leading & trailing spaces from the list of roles that was returned -->
<cffunction name="stripSpacesfromList">
<cfargument name="myList">
<cfset myArray=listtoarray(arguments.myList)>
<cfloop index="i" from="1" to="#arraylen(myArray)#" step="1">
<!--- <cfset myArray[i]=replace(trim(myArray[i]), " ", "_")>
out<br>--->
<cfset myArray[i]=trim(myArray[i])>
</cfloop>
<cfset newList=arrayToList(myArray)>
<cfreturn newList>
</cffunction>
This is the cfc that might be of interest to you:
<!---
This component implements methods for use for NT Authentication and Authorization.
$Log: NTSecurity.cfc,v $
Revision 1.1 2002/03/08 22:40:41 jking
Revision 1.2 2002/06/26 22:46 Brandon Purcell
component for authentication and authorization
--->
<cfcomponent name="NTSecurity" >
<!--- Authenticates the user and outputs true on success and false on failure. --->
<cffunction name="authenticateUser" access="REMOTE" output="no" static="yes" hint="Authenticates the user." returntype="boolean">
<cfargument name="userid" type="string" required="true" />
<cfargument name="passwd" type="string" required="true" />
<cfargument name="domain" type="string" required="true" />
<cftry>
<cfscript>
ntauth = createObject("java", "jrun.security.NTAuth");
ntauth.init(arguments.domain);
// authenticateUser throws an exception if it fails,
ntauth.authenticateUser(arguments.userid, arguments.passwd);
</cfscript>
<cfreturn true>
<cfcatch>
<cfreturn false>
</cfcatch>
</cftry>
</cffunction>
<!---
Authenticates the user and outputs true on success and false on failure.
--->
<cffunction access="remote" name="getUserGroups" output="false" returntype="string" hint="Gets user groups." static="yes">
<cfargument name="userid" type="string" required="true" />
<cfargument name="domain" type="string" required="true" />
<cftry>
<cfscript>
ntauth = createObject("java", "jrun.security.NTAuth");
ntauth.init(arguments.domain);
groups = ntauth.GetUserGroups(arguments.userid);
// note that groups is a java.util.list, which should be
// equiv to a CF array, but it's not right now???
groups = trim(groups.toString());
groups = mid(groups,2,len(groups)-2);
</cfscript>
<cfreturn groups>
<cfcatch>
<cflog text="Error in ntsecurity.cfc method getUserGroups - Error: #cfcatch.message#" type="Error" log="authentication" file="authentication" thread="yes" date="yes" time="yes" application="no">
<cfreturn "">
</cfcatch>
</cftry>
</cffunction>
<!---
This method combines the functionality of authenticateUser and getUserGroups.
--->
<cffunction access="remote" name="authenticateAndGetGroups" output="false" returntype="string" hint="Authenticates the user and gets user groups if it returns nothing the user is not authticated" static="yes">
<cfargument name="userid" type="string" required="true" />
<cfargument name="passwd" type="string" required="true" />
<cfargument name="domain" type="string" required="true" />
<cftry>
<cfscript>
ntauth = createObject("java", "jrun.security.NTAuth");
ntauth.init(arguments.domain);
// authenticateUser throws an exception if it fails,
// so we don't have anything specific here
ntauth.authenticateUser(arguments.userid, arguments.passwd);
groups = ntauth.GetUserGroups(arguments.userid);
// note that groups is a java.util.list, which should be
// equiv to a CF array, but it's not right now
groups = trim(groups.toString());
groups = mid(groups,2,len(groups)-2);
</cfscript>
<cfreturn groups>
<cfcatch>
<cfreturn "">
</cfcatch>
</cftry>
</cffunction>
</cfcomponent>
If the code from Brandon Purcell that uses the jrun.security.NTauth class doesn't work for you in cf9 (it didn't for me) the fix is to use the coldfusion.security.NTAuthentication class instead. Everything worked fine for me.
You could try following the guidance here: http://cfsilence.com/blog/client/index.cfm/2008/3/17/ColdFusionSharepoint-Integration--Part-1--Authenticating
Here is what it boils down to you doing:
edit the client-config.wsdd
Change
<transport
name="http"
pivot="java:org.apache.axis.transport.http.HTTPSender">
</transport>
to
<transport
name="http"
pivot="java:org.apache.axis.transport.http.CommonsHTTPSender">
</transport>
In my case I fixed this problem using 'NTLM Authorization Proxy Server'
http://www.tldp.org/HOWTO/Web-Browsing-Behind-ISA-Server-HOWTO-4.html
work fine for me :)