Wednesday, 14 March 2007

System.Net.WebRequest problems

This problem has been killing me. The overall problem is within a complex app, site and service installation, but i've scaled it right back to a simple web application that invokes a web request on another unsecured resource. My web app is very simple with just the following in Page_Load:

1
 

2
3
        System.Net.WebRequest tx = 
           System.Net.WebRequest.Create
              ("http://othermachine/listener.aspx");
System.Net.WebResponse rx = tx.GetResponse();
rx.Close();

This script is hosted on localhost and othermachine is within the intranet zone and no proxies etc are involved. On my machine things work fine, but on the other machines in the office (who also have the above installed on localhost) we the error message below during execution of the web request to the other machine (line 2 of the source);

A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond

Now, here's the kicker. the above code works when it's run through the VS.NET ASP.NET Development web server! It only fails when it's served through IIS!!! I've been trying to work this out for 2 days now to no avail. If anyone has any ideas, please get in touch....there's a beer in it for the solver!

UPDATE - HERE'S THE SOLUTION
Despite it working when hosted outside of IIS, and despite checking in several places to see if proxy / firewalls were to blame it turned out that when the above code was hosted within IIS it WAS using a proxy server on the failing machines. The solution was to simply disable the proxy for the app through web.config:

<system.net>
<defaultProxy enabled="false"/>
</system.net>

For some unknown reason, when hosted under IIS, the WebRequest would initialise and then ask the proxy server (configured in IE) for access to othermachine, but it would obviously ask the proxy server for this access as LOCALMACHINE\ASPNET or NETWORK SERVICE - which of course would probably be refused access to the proxy.

Really don't know why this was a problem under IIS only, but the above setting should work regardless of hosting environment.

Tuesday, 13 March 2007

The old double hop issue

Recently I had a WCF service calling into a simple ASMX service, but I got a 401 error each time it did so, even though my WCF service was setup to impersonate correctly.

The problem boils down to something called "double-hopping", which is where by default you're not allowed to pass network credentials beyond a single network hop.

Assuming the client application, the WCF and the ASMX are on separate servers, the client connects to the WCF, impersonating the client caller identity. When the WCF then connects on to the ASMX however it isn't allowed to pass up the impersonated credentials and instead uses the process identity for the worker thread (commonly ASPNET or NETWORK SERVICE).

Establishing trust for delegation is the answer - setting up the various servers to allow them to pass on impersonated credentials. The following support article at Microsoft covers how to configure everything: http://support.microsoft.com/default.aspx?scid=kb;en-us;810572

For reference here's a good posting about it.

Once you understand double hop issues, the problem is undeniably straight forward, however, if you're not "in-the-know" as I wasn't at the time I was struggling, double hopping in service oriented environments can be a royal pain in the ass.

Monday, 12 March 2007

WCF Impersonation

Impersonation (running code as the invoking user) under WCF is configured slightly differently to ASMX services. You need to first of all tell your binding to use windows authentication to identify the caller as follows;

    <bindings>
      <wsHttpBinding>
        <binding name="AuthBinding">
          <security>
            <transport clientCredentialType="Windows"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>

However, impersonation wise this won't do a thing - when your service code attempts to do something like write to the file system, it will do this as the user your worker processes are configured to use (normally ASPNET or NETWORK SERVICE).

Normally in ASMX you'd configure impersonation using web.config's <identity impersonate="true"/>, but WCF appears to ignore this. Instead you're individual methods in WCF need to be marked up by adding the following attribute onto the method;

[OperationBehavior(Impersonation:=ImpersonationOption.Required)]

When you consume the service on the client just add this code;

ClientCredentials.Windows.AllowedImpersonationLevel =
  System.Security.Principal.TokenImpersonationLevel.Impersonation;

The service will now impersonate the calling client user.

Thursday, 8 March 2007

Identifying Styles in Sharepoint Services 3.0

imageSharepoint has moved on leaps and bounds with the release of SPS 3.0, but it's still a pig when it comes to customisation. If you're changing the look and feel of a theme in SPS, you've got a tough time ahead! If it's available to you, using SPS designer aids with this greatly, but regardless, one of the hardest jobs in customising a theme is knowing which of the hundreds of styles apply to an element you want to change.

And so, in rolls the following code that you can re-use. By adding a content editor web part and using the source editor you can paste the following code in and have a web part that shows you the full CSS hierarchy of any element you move your mouse over:

<table border="1" width="100%">
<td valign="top" width="100%">
    <div id="spsClassTitle">TITLE</div>
    <div id="spsClassTree" style="height:300px;">CSS</div>
  </td>
</tr>
</table>

<script language="JavaScript">
function GetSPSClasses()
{
  var currElement = window.event.srcElement;
  var classTree = "";
  var Style1 = "color:green;";
  var Style2 = "color:red;";
  var CurrentStyle = Style1;
  
  if( currElement != null )
  {
    spsClassTitle.innerText = "Styles under the cursor";
    if( currElement.id != null && currElement.id != "" )
      spsClassTitle.innerText += "(" + currElement.id + ")";
      

    while( currElement != null )
    {
      if(currElement.className != null && currElement.className != "")
      {
        var newEntry = "<div style=\"" + CurrentStyle + "\"><B>" +
                       currElement.tagName + ":</B><BR/>" +
                       currElement.className + "</div>"
        if( classTree != "" )
          classTree = newEntry + classTree;
        else
          classTree = newEntry;

        if( CurrentStyle == Style1 ) CurrentStyle = Style2;
        else CurrentStyle = Style1;
      }

      currElement = currElement.parentElement;
      
    }
    
    spsClassTree.innerHTML = classTree;
  }
  else
  {
    spsClassTitle.innerText = "No styles under cursor";
    spsClassTree.innerText = "";
  }
}

window.document.body.onmouseover = GetSPSClasses;
</
script>

SQL and Remote Connections

Excuse the simplicity of this post but we've all been tripped up by SQL Server / SQL Express not accepting remote connections or not allowing SQL Logins and have had to hunt through our scratty notes to remember what we did last time we installed it. Well here's the definitive guide to getting SQL to accept remote connections and getting it to accept the sa (or other SQL user) login.

  1. Ensure TCP/IP is enabled in surface area configuration
  2. Ensure "enabled" for TCP/IP in Network Configuration -> protocols in SQL Server Configuration manager.
  3. Ensure sqlservr.exe exempt in firewall (if necessary)

That'll get it accepting remote connections. If you've installed with windows auth mode only though and you want to login with SQL users, you'll need to turn mixed mode on too:

  1. Open Enterprise Manager and open the server properties
  2. Set security to SQL Server and Windows auth mode
  3. If necessary enable the sa user:
    • Open security -> logins and open properties for the sa user
    • Select the status tab from the left hand side
    • Set Login: Enabled
  4. Restart SQL Server if you changed security at step 2

I know this was all very simple, but if nothing else I'll find it useful to refer back to when I try to connect and get a "connection refused" error and scratch my head for a bit.... [8-|]

Wednesday, 7 March 2007

WCF - Handling large data sizes

WCF is a bit fussy when it comes to protecting you against denial of service attacks by sending large data packets. This is great when you don't need to send large amounts of data, but when you do, it can cause you a few problems.

I've come across a situation recently sending multi megabyte attachments through a WCF service. In its default state WCF won't accept large data transmissions, you have to specifically configure it to accept large data by changing the binding as follows:

<binding name="AuthBinding" maxReceivedMessageSize="100000">

As the name implies maxReceivedMessageSize tells WCF how big a message it will be allowed to receive. If you're sending a large byte array across the wire - maybe an attachment file or something - you need to also configure the XmlReader that WCF uses to tell it how big an array to accept. You do this through the use of the readerquotas declaration within your binding:

<readerquotas maxArrayLength="10485760" />

This would allow the XmlReader to accept a byte array equivalent to a 10mb file.

Monday, 5 March 2007

FLUX.NET CMS v2.0 Released!

I released version 2.0 of the flux.net content management system and application framework yesterday. Go get it from http://www.fluxcms.co.uk/ if you haven't got it already!

FLUX.net is an open source content management platform and web application framework. It offers very advanced content and asset management facilities out of the box, coupled with an exceptional developers API and framework for building even the most complex web applications quickly and efficiently. Flux is presently in stable release 2.0 and 3.0 is in development.