Wednesday, February 29

SamlSecurityTokenRequirement enforceAudienceRestriction (AudienceUriMode) error when using BizTalk 2010 with a federated binding

When using a BizTalk 2010 WCF service in a federated environment, you may get the following non-descriptive error when you try to make a call into the BizTalk WCF service

"An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail".

This error unfortunately has nothing useful in the innerexception more often than not. Various security issues could cause this error (including certificate authentication). Turn on tracing on the BizTalk receive location by adding the following section into the web.config file for the service.
You can add it between the <connectionstrings /> and <system.web> nodes (just a guide)

    <system.diagnostics>
      <sources>
            <source name="System.ServiceModel" switchValue="Error, Warning, Verbose" propagateActivity="true">
            <listeners>
               <add name="traceListener"
                   type="System.Diagnostics.XmlWriterTraceListener"
                   initializeData= "c:\log\BizTalktraces.svclog" />
            </listeners>
         </source>
      </sources>
  </system.diagnostics>
Make sure to create a "log" folder at the root of your c: drive as the configuration suggests. For the purpose of this troubleshooting, you can also grant full permissions to everyone on the folder to make sure it can be written to.

When you try to run the service again and check the BizTalktraces.svclog file in a text editor, you might find the following lines in the stack trace:

<InnerException><ExceptionType>System.InvalidOperationException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>ID1032: At least one 'audienceUri' must be specified in the SamlSecurityTokenRequirement when the AudienceUriMode is set to 'Always' or 'BearerKeyOnly'. Either add the valid URI values to the AudienceUris property of SamlSecurityTokenRequirement,  or turn off checking by specifying an AudienceUriMode of 'Never' on the SamlSecurityTokenRequirement.</Message>

By default the federation binding will typically add a federationServiceHostConfiguration extension to the servicebehaviors section.

Open the receive location 'Behavior' tab, and click on the 'IssuedTokenAuthenticaiton' node. Set the audienceUriMode to 'Never'


Remove the federationServiceHostConfiguration extension from the configuration (should be in the same tab, above 'serviceMetadata', but has already been removed in the picture above).

This should stop the errors.

BizTalk 2010 message submission with transport-level security to SharePoint 2010 forms library without the WSS adapter - Part 2

The Form submittal WCF service

In this post I will try to briefly explain the steps needed to create a WCF service to submit BizTalk messages into a SharePoint 2010 form library

The steps are basically as follows:
1. Create a WCF service
2. Add a service reference to the SharePoint Copy service

The SharePoint environment includes built-in Web services in the Windows SharePoint Services that can be used to work with areas of the SharePoint object model remotely. Each Web application Web service is installed in the _vti_bin directory, which maps to the following file system location:
%COMMONPROGRAMFILES%\Microsoft Shared\web server extensions\12\ISAPI

These services allow developers to manipulate information stored in SharePoint sites. Msdn lists these services on this link

The WCF service below calls one of these services, namely the Copy web service, using the CopyIntoItems operation to post xml data into a form library hosted within a SharePoint site. the URL for the service(s) is typically relative to the site you wish to manipulate. e.g. http://sharepointservername/sharepointsitename/_vti_bin.copy.asmx

Below is the cample code for a WCF service which uses a service reference to call the copy service for my sharePoint site (this is very basic first-draft code showing the main functionality of the service, derived and slightly modified from R Seroter's example in his post, which I referred to in part 1). This is not a tutorial on WCF services, so please read about WCF services to understand the service better.

The Copy web service expects a few parameters:
  • The source uri, 
  • the destination path (including the SharePoint site URL, the form library name, which is the name of the form library created in part 1 and the filename you wish to call the form document when it is displayed in SharePoint),
  • the xml file itself sent as bytes,
  • an array of SharePoint properties related to the submitted xml
  • and the user credentials of a user with permissions to create the form in the SharePoint site. In this case, I pass the credentials in directly via the ClientCredentials of the proxy. I added the credentials into the web.config file as keys in the Appsettings section and called them within the code. That way, they can be changed when needed if the permissions in SharePoint need to be changed

ISendToSharePointFormsService.cs

using
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
 namespace
{

 [SendToSharePointServiceContract(Namespace = http://mynamespace/biztalk.wcf.SendToSharePoint/v1.0)]
 public interface ISendToSharePointFormsService
 {
  [OperationContract]
  string SendXmldoc(string strFile, string fileName, string servertouse, string librarytouse);

  [OperationContract]
  string GetXmlToString(string strFile);

 }
}


 

SendToSharePointFormsService.svc.cs

using
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.IO;
using System.Xml;
using System.Net;
using System.Configuration;

namespace SendToSharePoint
{
  public class SendToSharePointFormsService : ISendToSharePointFormsService
  {
    public SendToSharePointFormsService()
    {
    }

    public string SendXmldoc(string DocinStringForm, string fileName, string servertouse, string librarytouse)
    {
     //declare variables
      string siteRoot, docLibrary, uname, pwrd, domn;

      //read appsettings for vars needed for sending file to SP
      try
      {
        siteRoot = servertouse;
        docLibrary = librarytouse;
        uname = ConfigurationSettings.AppSettings["UsernameApprovals"].ToString();
        pwrd = ConfigurationSettings.AppSettings["PasswordApprovals"].ToString();
        domn = ConfigurationSettings.AppSettings["userdomain"].ToString();
      }
      catch (Exception apps)
      {
        return "Error reading configuration settings from app.config - " + apps.InnerException;
      }

      //send to library
      string finalresult = UploadXmlToSharePoint(DocinStringForm, siteRoot, docLibrary, fileName, uname, pwrd, domn);

      return finalresult;

    }

    #region Actual sender
    private string UploadXmlToSharePoint(string docToAdd, string siteRoot, string docLibrary, string fileName, string userName, string password, string domain)
    {

      //build full destination Url
      string destinationPath = siteRoot + "/" + docLibrary + "/" + fileName;

      //convert the xml string sent from BizTalk into a byte array
      byte[] fileIn = ConvertDocToBytes(docToAdd);

      //holds MOSS service response values
      SharePointSvc.CopyResult[] results;
     
      //required fieldinformation array
      SharePointSvc.FieldInformation fieldInfo = new SharePointSvc.FieldInformation();
      SharePointSvc.FieldInformation[] fieldInfoArray = { fieldInfo };

      //destination url (notice that it's an array, meaning //multiple sites COULD be targeted
      string[] destUri = { destinationPath };

      //create instance of web service proxy
      SharePointSvc.CopySoapClient copycl = new SharePointSvc.CopySoapClient();

      //pass valid credentials
      copycl.ClientCredentials.Windows.ClientCredential = new NetworkCredential(userName, password, domain);

      //copycl.ClientCredentials.Windows.AllowNtlm = true;
      //call primary operation; sourceUri, doesn't matter here

      copycl.CopyIntoItems("http://none", destUri, fieldInfoArray, fileIn, out results);
     
      //check for error and return final result
      if (results[0].ErrorMessage != null)
      {

        return "Error: " + results[0].ErrorMessage;
      }
      else
      {
        return "Success Sending File to SharePoint at " + System.DateTime.Now.ToString();
      }

  }

   #endregion 
 
 #region conversions
   
     //bytes
    public byte[] ConvertDocToBytes(string xmlString)
    {
     ASCIIEncoding encoding = new ASCIIEncoding();
     return encoding.GetBytes(xmlString);
    }
    
    #endregion
  }
}



Host the SendToSharePoinForms WCF service in IIS (I believe there are numerous posts on the Internet on the various methods of hosting a WCF service in IIS)

Go on to Part 3 - Posting the forms from inside a BizTalk orchestration

Friday, February 24

SharePoint/InfoPath cross-domain submission issue

When you use InfoPath to design a form for data submission to a wcf service (or web service) and host it in a SharePoint form library, you may get the following error when trying to submit the form data to a service hosted within the same domain:
"The form cannot be submitted because this action would violate cross-domain restrictions.
If this form template is published to a SharePoint document library, cross-domain access for user form templates must be enabled under InfoPath Forms Services in SharePoint Central Administration, and the data connection settings must be stored in a UDC file in a data connection library in the same site collection"

This issue is a cross between the way InfoPath stores URL information in your data connections and the way SharePoint resolves URLs.

The most likely cause of this would be the form being publishd to SharePoint server using a URL in the format http://servername/sitename.domain.com, and a user trying to access the form via the url http://servername/sitename/libraryname/... etc

To resolve this issue, a mapping needs to be made to resolve http://servername/sitename/libraryname/ to point to http://servername/sitename.domain.com

To do this, go to SharePoint Central Administration, click on System Settings, click on 'Configure alternate access mappings' under Farm Management and ten click on 'Add Internal URLs' to add the mappings. It is mostly self-explanatory from this point

Friday, February 17

Notes on Upgrading from BTS 2006 to BTS 2010

One or two (or more) common but baffling errors you may come across when upgrading from BizTalk server 2006 to BizTalk server 2010


mismatched 'braces''{}' error or 'unexpected EOF' error.

This might suggest you have omitted to close some braces ({}) in your expression shapes, but it is actually a bug identified by Microsoft at this link http://support.microsoft.com/kb/2548064

Go to this link http://support.microsoft.com/kb/2573000 and click on 'View and request hotfix downloads' to request the hotfix from Microsoft (this should be emailed to you within 5 minutes after filling the form)

Download the application from the link provided in the email from Microsoft

Double-click on the exe file and extract it to a location on your computer

Close all instancs of Visual Studio and click on the exe file extracted to the location to install the hotfix


"Failed to load "" Type error on a send port

When using web references from web services to support legacy systems, you may use either a web reference message type or web reference port type in your orchestration.

This error is most likely due to the fact that you have matched a web reference message type to a logical port that is not of the same web reference port type in your orchestration, or matched a web reference port type to a message type not derived from the same web reference in your orchestration

You need to ensure that your web reference messages are matched with port types derived from the same web reference

Thursday, February 2

Implementing a BizTalk WCF Service using TransportWithMessageCredential Security

I remember that I couldn't really find any detailed posts on how to do this particular mode when I needed to do it on a project a few years ago. I had to figure it out then. Whilst implementing again recently, I decided to do a quick post on it.


Note: Ideally, you should already have a site in IIS configured with a certificate. Read this and this on how to obtain certificates. Please read this for how to add a certificate to a site. You will also need a second certificate (or the same one for the purposes of this exercise) for use as the service certificate. This should be installed in the LocalMachine certificate store. Once you have the service certificate, copy the thumbprint and keep it for use later

Copy service certificate thumbprint

On the machine hosting the WCf service, open the Microsoft Management Console (Click Start, type mmc in the search field and enter). Click on File and click on Add/Remove Snap-in…


Click on ‘Certificates’. Click on ‘Add’



Make sure ‘Computer Account’ is selected and click on Next. ‘Local computer’ should be selected by default on the next screen. Click finish to view the certificates installed on the computer





Open the Personal folder and right-click the Certificates folder to see the certificates installed on the LocalMachine. Double-click the certificate you wish to use, go to the ‘Details’ tab and click on ‘Thumbprint’. Copy the thumbprint value and paste it in a new notepad file. Remove all the spaces between the characters (Paste the thumbprint in Notepad, click on edit, replace, hit the spacebar and click on replace all to remove all spaces) and save the file somewhere for later use (this will be used in BizTalk later)



You now need to make the certificate available to the WCF host process (see this msdn article for an explanation of the scenarios in which you would need this). Click OK to go back to the personal certificates store, right-click on the certificate, click on ‘All Tasks’ and click on ‘Manage Private Keys’


In the next window, click on ‘Add’, and type in the BizTalk service account that will run the WCF service process (basically the applicaiton pool user) in the format <domain\user>. Click ‘Check Names’ to get the user credentials and click OK to add the user.


Click on the newly added user . In the permissions window, untick the ‘Allow’ box next to the Full Control permission. Make sure the Read permission box is ticked



Click Apply. Click OK and close the mmc window. Save the mmc snap-in as ‘cert’ when prompted in case you need to use the snap-in at some other time. It will save in the Administrative Tools folder by default so you can open it from there at some other time
Create a BizTalk WCF service (You can do this using the BizTalk WCF Service Publishing Wizard). Whilst creating the service, make sure you create a BizTalk receive location in the BizTalk Application that will be implementing your service. Also publish the WCF service into a site configured for SSL and change the application pool for the application (not the site) to an application pool using the user you granted permissions to earlier. In this case, my WCF service has been published into the default website, which has been bound with a certificate for SSL. See msdn for instructions on adding a certificate to a site


Open IIS Management Console (Usually Start , Administrative Tools, Internet Information Services (IIS) Manager). Open the site hosting your WCF application (e.g. Default Web Site). Right-click on the application and click on 'Explore'. This should take you to the application/service folder.



Open web.config in a text editor like Notepad (or Notepad++ ideally). Make a slight modification to the ServiceModel section by changing the httpsGetEnabled attribute of the serviceMetadata node from false to true as shown below.

<system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviorConfiguration">
          <serviceDebug httpHelpPageEnabled="true" httpsHelpPageEnabled="false" includeExceptionDetailInFaults="true" />
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
        </behavior>
      </serviceBehaviors>


Save and close the file. Close the IIS Management console.

Open the BizTalk Management Console (Start à All Programs à Microsoft BizTalk Server 2010 à BizTalk Administration)


Browse to the receive location that will be implementing your service endpoint



Open the receive location and click on ‘Configure’.



The endpoint URL should already be there. Click on the ‘Binding’ tab and select ‘WSHttpBinding’ from the binding dropdown



Click on ‘Security’ and select ‘TransportwithMessageCredential’ as the security mode



Open the security options node, click on ‘Transport’ and select ‘Certificate’ as the ClientCredentialType



Click ‘Message’ and the default ClientCredentialType of Windows should be selected. (If you have non-repudiation requirements, 'Certificate' clientCredentialType should be selected here instead of Windows. this will also require using a certificate for the client when the service is called). Also make sure negotiateServiceCredential and establishSecurityContext are set to true if you wish to use secure sessions for SCTs. For the sake of this exercise, set them to true



If you have a reliable messaging requirement, Click on ‘ReliableSession’ and make sure ‘enabled’ is set to true



Click on the ‘Behavior’ tab. Right-Click on ‘ServiceBehavior’ and click on ‘Add extension’. In the window that opens up, select ‘ServiceCredentials’ and click OK



Open the new ‘ServiceCredentials’ node. Click on ‘ServiceCertificate’ and copy in the certificate thumbprint you saved earlier from the notepad file into the ‘findValue’ field. Also set the values in the other 3 properties as highlighted in the diagram below



Click OK and Apply to finish configuring the service


If you have non-repudiation requirements, or you used 'Certificate' as the message clientCredential mode stage we configured earlier for any reason, your client application will need to have a certificate added. You can add the certificate to the client application in various ways:

Either to the behaviorConfiguration property of the client endpoint in binding in the app.config after consuming the WCF service:
<endpointBehaviors>
     <behavior name="PARolesService.EndPointBehavior">
        <clientCredentials>
          <clientCertificate findValue=" ade886aa2964bbd8678161102f583e1e894ea560" storeLocation=”LocalMachine” StoreName=”My” x509FindType="FindByThumbprint" />
          </clientCredentials>
      </behavior>
</endpointBehaviors>

or directly in code in the following way:

ServiceReference.TransportwithmessagecredServiceClient cl = new ServiceReference.TransportwithmessagecredServiceClient("WSHttpBinding_ServiceHTTPS"); 
cl.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "ade886aa2964bbd8678161102f583e1e894ea560");
where cl is your client proxy, ServiceReference is the name of the service reference used to add the WCF service, and WSHttpBinding_ServiceHTTPS is your endpoint name in app.config