ColdFusion/Sharepoint Integration - Part 1 - Authenticating

Posted By : todd sharp Posted At : March 17, 2008 11:04 AM Posted In: Sharepoint, ColdFusion

30

I've been working a bit with Sharepoint lately for some team sites at work and needed a way to dynamically manage some user lists for a given site. Since Sharepoint has a pretty extensive API for interacting with the various bits I figured it would be pretty simple to create a scheduled task in ColdFusion that would pull a list of valid userid from my database and pass the list to Sharepoint to keep the authentication up to date without much intervention on my behalf.

Turns out it wasn't quite all that simple to even connect to the Sharepoint service. My attempts at setting/passing my NT credentials on the webservice call kept resulting in a 401 (Unauthorized) response from the service. Eventually I came across this post from Tom Jordahl that presented a fix. Let me quote Tom's post since he does a good job explaining:

Someone asked me how they could turn on NT Authentication for web service using Apache Axis (the web service used by ColdFusion). By default Axis uses its own HTTP client code, org.apache.axis.transport.http.HTTPSender, to send the XML/SOAP POST requests to a web service. This uses HTTP 1.0 and generally works file.

Axis also supports the Jakarta Commons HTTPClient library, and has since 1.0. To configure Axis to use this instead of its own library you must edit the client-config.wsdd file used by Axis. It gets found on the classpath and generally you don't actually have one and the one built in to axis.jar gets used. The interesting line is the http transport. To switch Axis to use the HTTPClient jar, you would change this:

<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender"></transport>
To this:
<transport name="http" pivot="java:org.apache.axis.transport.http.CommonsHTTPSender"></transport>
Once you have this code configured, Axis will use the HTTPClient library for it HTTP needs. Since the HTTPClient library supports NT Authentication, you just set the username/password on the Stub object as you would normally do for (say) Basic Authentication and it will just work. If you are talking to a .NET web service, you are done.

So as he says, I made the change and "it just worked". I can now interact with the Sharepoint user service. Honestly, I haven't done a thing other then confirm that I can now authenticate at this point. Which is why I named this 'Part 1' - because I have a sinking suspicion that my journey to CF/Sharepoint enlightenment will have a few other bumps along the way.

As a side note, I am a bit unhappy that I had to mod the client-config.wsdd file to get this to work. If anyone knows of a simpler workaround please let me know.

Comments (30)

Steve Hammonds's Gravatar Can you call a sharepoint webservice with CF 7.1? I made the change you suggested but I can't get past an initial error "Could not generate stub objects for web service invocation" which I believe means it is not event getting to the wsdl. I have spent two days trying to find a working snippet of code on the internet for doing ANYTHING with sharepoint from CF. I got nothing. Here is the code that gives me the aforementioned error:


<Cfset ws=createobject("webservice","http://websitename/sites/sitename/_vti_bin/lists.a...;)>
<Cfdump var="#ws#">

todd sharp's Gravatar Dumb questions:

Did you try the url directly in your browser to make sure you can view the WSDL?

Did you restart CF after making the change to the config file?

Steve Hammonds's Gravatar Yes, I restarted CF. Typing into my browser works fine too except:

From my desktop I do not have to authenticate in the browser. When I do it from the server (local admin logged in, CF running under with another domain users credentials), I do have to autheticate with my domain credentials.

I don't know how to make this authentication happen in my code. The examples I have seen set the username password AFTER creating the webservice. I can't get that far.

Kier Simmons's Gravatar I made this change and there was no difference in my inability to call sharepoint webserives. We need a much much much more detailed explaination of each step required to make this work.

todd sharp's Gravatar There will be a detailed article in the next edition of FAQU. Make sure you subscribe.

Jenn's Gravatar When does the next issue of FAQU come out? For that matter is it at all possible to get a preview of this "detailed article" being as I've got a project that's due in a week and a half using this integration and I'm having a devil of a time with it?

todd sharp's Gravatar Jenn:

Check your email.

Todd

Jenn's Gravatar Todd, thanks again for the help. I've got things connecting and calling and returning and that's great. But I've hit another stumper. This blog seems to get better responses than others I've tried, so here's the question. I have to call the "UpdateListItems" method. It appears to take two parameters, first a String and second an XML snippet that contains the information about what to update. Seemed straightforward enough to call it, but it turns out that passing in the correctly formatted XML as the second parameter results in ColdFusion being unable to find the method call. When I generated the stubs and looked at them closely it looks like that second parameter is actually a microsoft object that contains an array of axis message objects. Any idea how you're supposed to pass that? Most of the list manipulation methods take these "XMLNode" input parameters, but passing XML to them does not actually work. I'm at a complete loss.

Jenn's Gravatar Steve,
I don't know if you're still following this post or not, but I bet your problem is one that I was having earlier. If the WSDL is actually using NTLM then you can't authenticate against it and you can't get the stubs to generate. To solve the problem access the WSDL from your browser and save it into a folder on your webserver. Then point your CFObject call to the file you've saved on your server. This will let the stubs generate but since the WSDL came off the sharepoint server originally all the method calls will still go to sharepoint. (I can't remember whose blog I found this tidbit on but it sure does work).

todd sharp's Gravatar Jenn:

Can you email me an example of your call to the service and the error that is being thrown?

Todd

todd sharp's Gravatar PS...not sure this makes a difference for your particular problem, but as a general rule I'd suggest using the TCP monitor when working with any web service. It will totally save your sanity sometimes...

http://livedocs.adobe.com/coldfusion/7/htmldocs/ww...

Neil Merton's Gravatar We've got a project a work on the go at the moment where we need to try and interact with CRM 4 web services. It sounds as though the details article you wrote may have the answers to us having difficulty getting ColdFusion to talk to the CRM 4 web services. Is there any chance that you could email the article to me? Many thanks in advance.

todd sharp's Gravatar Thanks to some idiots who are comment spamming this post non stop I have closed it for comments. If you would like to discuss this post please use my contact page.

share point's Gravatar Hi Great Article, I'm new to Sharepoint and this is a great reference to have for our implementation and my planning

Charlie Arehart's Gravatar Hey Todd, thanks for another great blog entry. Quick question about this (seems the comments are open again).

You mention getting success with the approach Tom offered, but it seems you had to (as he said) pass the username/password. What if we don't want to prompt the user for that (or retrieve it from somewhere we stored it for them)? Is there any way to pass it along on their behalf?

I've also asked on Tom's blog entry you point to, but I don't know if he'll respond.

Also, did you ever do a part 2? I couldn't find it.

Todd Sharp's Gravatar AFAIK, there's no way around sending the auth credentials with each call.

Also, the next FAQU has my article on CF/SharePoint with some pretty detailed examples (which is essentially part 2 of my blog post). It got bumped from the last issue for some reason, but I'm 95% sure it'll be in the next one that's coming out soon.

jay's Gravatar I noticed the new FAQU is out, but I don't see this subject in any of the headlines. (Or any articles by Todd). Do you know if it's in there? I want to make sure before I subscribe.

todd sharp's Gravatar @Jay - I don't think the issue has come out yet...I'm checking with Judith to see when it is scheduled to ship. I'll let you know.

jay's Gravatar Thanks for the info todd, I've been trying to link these things together for a while now without success. As jenn mentioned, I stored the WSDL on the CF server to get around the authentication but i cant seem to pull any webservice functions.

Could you or jenn post (or email) some more tips on how you guys got this working? It would be a life saver!

Jenn's Gravatar Jay,
I'd be glad to give you some more help...
I was going to try and email you, but you're address isn't in the post that I see. If you want to send me a line at jdodson@pioneerservices.com feel free. If you include in the email what errors (if any) you're getting that would also be helpful. This project was one of the most frustrating I've ever done so I'd like to save someone else some of the steps.

Mona's Gravatar Hello,

There is so little info on the internet about this, I will try and ask you my question right here.

I have a little colfusion page that I view through a Page Viewer Webpart in Sharepoint. The only thing I want from sharepoint is the logged in user. I found bits of code to get that, but it's always from .NET, I don't know how to do the same thing with Coldfusion. Any ideas?
Thanks,

todd sharp's Gravatar I'm sorry that I've been less then helpful with all of the comments on this post. I've actually not had to do anything with SharePoint since I posted this so I'm afraid I don't have any experience to share here. I would like to recommend that anyone who needs to work with ColdFusion and SharePoint should check out ColdFusion 9 which has a new cfsharepoint tag that supposedly helps with many of the issues that folks have. You can download the beta of CF9 here:

http://labs.adobe.com/technologies/coldfusion9/

Good luck!

Dica's Gravatar Hello,

I've started working on a project that will interact with Sharepoint, but unfortunately my company is stuck with ColdFusion 7. So, no cfsharepoint tag for me... When did the FAQU article come out?

Thanks,

Anthony Ortega's Gravatar I work for a company that just upgraded to cf 8 right before cf 9 went live. I need to interact with the sharepoint services and was woundering if you could point me in the direction of the FAQU you mentioned above.

Many thanks

Dica's Gravatar Hi Jen, did you figure out how to invoke the UpdateListItems method?

Jenn's Gravatar Dica,
We did figure it out. We used what I feel is kind of a crazy mix of java and coldfusion and it worked out fine. Basically we've got a CFC with a whole bunch of inline java (what a nifty thing to be able to do). It's been working like a champ for almost two years now. If you have a specfic thing you're trying to do let me know and I'd be glad to help you out... though I haven't been in that code for awhile so I'm kind of rusty on it.

Dica's Gravatar Hi Jen,

Thank you for taking the time to reply. We are trying to use UpdateListItems to do two things: add an item to a list and upload a document to a document library. However, we haven’t been able to figure out how to format the second parameter of the method. As you mentioned earlier, according to the documentation, it looks pretty simple: a piece of XML.

<Batch>
<Method ID='1' Cmd='New'>
<Field Name='Title'>First item of list</Field>
</Method>
</Batch>

However, no matter how we set the xml argument, we get the error message “Web service operation UpdateListItems with parameters {parameters_here} cannot be found.” We tried adding some inline java but got a type mismatch error.
I would truly appreciate it if you could shed some light into how we need to pass this parameter...

Once again, thanks.

Dica's Gravatar Hi Jen,

I've been trying to consume UpdateListItems since your last email and I'm still getting nowhere. You mentioned you used some inline Java. Can you provide more details?
Thank you,

Jenn's Gravatar I've been asked again how we did this and I still ahven't had time to really put together a package that I can post. but here's an example update List Item function that we're actually using which may point o you int he right direction. (wonder if putting code in here is going to work).

<cffunction name="updateListItems" output="true" returnType="String" hint="Updates the items in a list or adds new items based upon the contents of the updates array">
<cfargument name="listGuid" type="String" required="Yes" hint="The GUID of the list being updated">
<cfargument name="listVersion" type="String" default="1" hint="the List version, in case there is more than one">
<cfargument name="viewName" type="String" default="" hint="The view to use when updating... normally left blank">
<cfargument name="updates" type="any" required="Yes" hint="The actual update information in the form of a struct">

<cfscript>
var doc = "";
var batchXML = "";
var updateElement = "";
var curMethod = "";
var finalParam = arrayNew(1);
var xmlResponse = "";
var xPath="/ns1:Results/ns1:Result/ns1:ErrorText"; //Look for the error text nodes
var errorList = "";
</cfscript>

<CFOBJECT type="java" class="com.microsoft.schemas.sharepoint.soap.UpdateListItemsUpdates" name="Element" />
<CFOBJECT type="java" class="javax.xml.parsers.DocumentBuilderFactory" name="DocumentBuilderFactory"/>
<CFOBJECT type="java" class="org.apache.axis.message.MessageElement" name="MessageElement">

<cfscript>
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
batchXML = doc.createElement("Batch");

batchXML.setAttribute("OnError", "Return");
batchXML.setAttribute("ListVersion", "1");
batchXML.setAttribute("ViewName", "");
</cfscript>
<cfloop from="1" to="#ArrayLen(updates)#" index="i">
<cfset curMethod = buildMethodElement(doc,updates[i][1],"#i#",updates[i][2])>
<cfset batchXML.appendChild(curMethod)>
</cfloop>
<cfscript>
finalParam[1] = MessageElement.init(batchXML);
updateElement = Element.init(finalParam);
</cfscript>

<cfinvoke webservice="#sList#" method="UpdateListItems" returnVariable="Results">
<cfinvokeargument name="listName" value="#arguments.listGUID#"/>
<cfinvokeargument name="updates" value="#updateElement#">
</cfinvoke>

<cfset xmlResponse = Results.get_any()>
<cfset xmlResponse = xmlParse(xmlResponse[1])>
<cfset FieldArray = XmlSearch(xmlResponse,xPath)>

<cfloop from="1" to="#ArrayLen(FieldArray)#" index="i">
<cfset errorList = FieldArray[i].XmlText>
</cfloop>
<cfreturn errorList>
</cffunction>

You will need to get the appropriate java classes on your class path. We did that by sticking them in a folder called stubs and putting the whole thing on the path. You can get the clases from the Sharepoint server I believe.

Good luck

David's Gravatar I was having similar authentication problems getting cfsharepoint to talk to my WSS 3.0 web services. I kept getting 401 authentication errors even though I had enabled basic authentication along with NTLM on my vti bin directory via IIS.

Turns out the solution to all my problems was to enable basic authentication via the SharePoint's Central Administration under Application Management > Authentication Providers. Once you go into the detail for your zone (this will probably be the "default" zone) you just go down and check Basic authentication. In my case I had only NTLM Integrated authentication checked.

Hope this helps someone else having authentication problems with cfsharepoint. You CAN use it with NTLM as long as you ALSO have basic authentication checked.

P.S. I tried the trick above changing the client-config.wsdd file used by Axis and it made no difference. The key was enabling basic auth via sharepoint central admin.