Ronald
terug naar het overzicht

One of the programming      guys

Ik ben sinds 2000 bezig met programmeren, eerst in Java, daarna in C# en de laatste tijd ook F#. Gedurende die tijd heb ik altijd geprobeerd op de hoogte te blijven van de laatste ontwikkelingen op IT gebied. Het leukst aan mijn werk vind ik het vertalen van klantwensen in oplossingen. Die overigens met of zonder programmeerwerk bereikt kunnen worden.

.NET 4.5 WebSocket client without a browser

door Ronald 27-7-2012

As you may have noticed, Microsoft added WebSocket support to Internet Explorer 10 and to IIS8, both available on Windows Server 2012 and Windows 8. The usual WebSocket demo’s all assume a browser as the client but that’s not absolutely necessary: the protocol doesn’t exclude non-browser clients.

 

To support non-browser clients, .NET Framework 4.5 includes a new namespace and classes to support writing WebSocket clients: System.Net.WebSockets. I’ll demonstrate how to use these new classes by implementing a simple WebSocket chat server hosted in IIS8 that broadcasts every message it receives to all clients. The client will be a console application that uses classes from the new namespace. Because I was curious about what actually gets sent across the wire, I used Fiddler to intercept the communication between client and server.

 

Server

First of all: the server. I used an ASP.NET MVC4 ApiController for accepting WebSocket requests:

using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using Microsoft.Web.WebSockets;
 
namespace WebSockets.Controllers
{
  public class WebSocketsController : ApiController
  {
    public HttpResponseMessage Get(string name)
    {
      HttpContext.Current.AcceptWebSocketRequest(
            new ChatSocketHandler(name));
      return new HttpResponseMessage(
            HttpStatusCode.SwitchingProtocols);
    }
 
    private class ChatSocketHandler : WebSocketHandler
    {
      private static readonly WebSocketCollection Sockets =
            new WebSocketCollection();
 
      private readonly string _name;
 
      public ChatSocketHandler(string name)
      {
        _name = name;
      }
 
      public override void OnOpen()
      {
        Sockets.Add(this);
        Sockets.Broadcast(
              string.Format("{0} joined.", _name));
        Send(string.Format("Welcome {0}.", _name));
      }
 
      public override void OnMessage(string message)
      {
        Sockets.Broadcast(
              string.Format("{0} says: {1}", _name, message));
      }
 
      public override void OnClose()
      {
        Sockets.Remove(this);
        Sockets.Broadcast(
              string.Format("{0} left.", _name));
      }
    }
  }
}

The HttpContext class defines two overloads of AcceptWebSocketRequest that accept a Func<AspNetWebSocketContext, Task>. To make things easier, the Microsoft.WebSockets NuGet package provides an extension method, also conveniently named AcceptWebSocketRequest and a base class WebSocketHandler that wrap the creation of the Func<AspNetWebSocketContext, Task> and provide some methods to override like OnOpen and OnMessage.

 

To enable WebSocket support on IIS8 on Windows Server 2012 you have to configure the IIS role and some associated features. The configuration I use can be seen in the two screenshots below.

 

Roles Features

 

Client

The client is a simple console application that gives a user the opportunity to send messages to the server and simply shows all messages it receives. This code is a little longer so I’ll split it up. I’ll attach a zip file containing the entire VS2012 project. First of all: connecting to the WebSocket server:

var cts = new CancellationTokenSource();
var socket = new ClientWebSocket();
string wsUri = string.Format(
  "ws://rwwildenvs2012/WebSockets/api/websockets?name={0}", name);
await socket.ConnectAsync(new Uri(wsUri), cts.Token);

This code creates a new ClientWebSocket instance and uses the ConnectAsync method to connect to the specifief URI. The ClientWebSocket does not expose any synchronous methods except Dispose and Abort. The URI has several components, described in the RFC. The name part of the query string is passed as the name parameter to the Get method of the WebSocketsController.

 

Next part is a receive loop where we receive messages from the server:

Task.Factory.StartNew(
  async () =>
  {
    var rcvBytes = new byte[128];
    var rcvBuffer = new ArraySegment<byte>(rcvBytes);
    while (true)
    {
      WebSocketReceiveResult rcvResult =
          await socket.ReceiveAsync(rcvBuffer, cts.Token);
      byte[] msgBytes = rcvBuffer
          .Skip(rcvBuffer.Offset)
          .Take(rcvResult.Count).ToArray();
      string rcvMsg = Encoding.UTF8.GetString(msgBytes);
      Console.WriteLine("Received: {0}", rcvMsg);
    }
  }, cts.Token, TaskCreationOptions.LongRunning,
     TaskScheduler.Default);

The core of this loop is the ReceiveAsync method that fills a byte buffer and gets the message from this buffer. In the real world this receive loop would be more complex. The data framing part of RFC 6455 allows for messages to be sent in multiple parts (WebSocketReceiveResult.EndOfMessage) and a message can be a closing message instead of a data message (WebSocketReceiveResult.CloseStatus).

 

Finally we’d like to send messages ourself so we build another loop that allows the user to enter messages to send:

while (true)
{
  var message = Console.ReadLine();
  if (message == "Bye")
  {
    cts.Cancel();
    return;
  }
  byte[] sendBytes = Encoding.UTF8.GetBytes(message);
  var sendBuffer = new ArraySegment<byte>(sendBytes);
  await socket.SendAsync(
      sendBuffer,
      WebSocketMessageType.Text,
      endOfMessage: true,
      cancellationToken: cts.Token);
}

The core part here is the SendAsync method. It accepts the message to send, the message type (text, binary or close), whether this is the closing part of a message that was sent in multiple parts and a cancellation token. Since we only sent single-part messages in this example, the message we send is always the end of the message.

 

Result

Now, what would all of the above look like when run? Below you see a ‘conversation’ between three clients: Ronald, John and Adam.

 

Ronald

Ronald is the first to enter the conversation and the first to leave.

 

John

John enters as the second participant.

 

Adam

Adam is the third participant and the last one to leave.

 

On the wire

Every WebSocket conversation starts with a HTTP handshake that is defined in RFC 6455. When the handshake is complete, client and server have established a TCP connection that each one can use to send messages on. In Fiddler, the handshake looks like this:

 

handshake

 

Some interesting things to note in the request:

  • The Sec-Websocket-Key HTTP header field. This is a new HTTP header field for the WebSocket protocol and its value is a base64-encoded random 16 byte nonce.
  • The Sec-Websocket-Version HTTP header field that is (for now) required to have the value 13.
  • The Upgrade HTTP header field. This is the field that informs the server that a client wishes to establish a WebSocket connection.

And in the response:

  • The 101 Switching Protocols response that is required by the protocol. If a 101 is not sent, it means that the handshake has not yet completed and that HTTP semantics still apply. For example, the server may sent a 3xx redirect response.
  • The Sec-WebSocket-Accept HTTP header field. This is the server’s response to the client’s Sec-Websocket-Key header. It is constructed by taking the client key, appending the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" to it, taking the SHA-1 hash of the result and base64-encoding the hash. The client must validate that it has the correct value (see section 4.2.2 of the spec, bullet 5.4).

 

Once the handshake is completed, client and server have a bidirectional communication channel that is used for data and control messages. Below is a small Fiddler trace of the previous conversation, starting from the moment John joined.

   1: 19:28:31:3477 [WebSocket #170] Server->Client (14 bytes)
   2: TYPE: TEXT.
   3: MESSAGE: John joined.
   4: FLAGS: 10000001 DATA: 12 bytes.
   5: ------------------------------
   6: 19:28:31:3497 Upgrading Session #183 to websocket 
   7: 19:28:31:3677 [WebSocket #183] Server->Client (29 bytes)
   8: TYPE: TEXT.
   9: MESSAGE: John joined.
  10: Welcome John.
  11: FLAGS: 10000001 DATA: 12 bytes.
  12: ------------------------------
  13: 19:28:40:4195 Upgrading Session #185 to websocket 
  14: 19:28:40:4215 [WebSocket #183] Server->Client (14 bytes)
  15: TYPE: TEXT.
  16: MESSAGE: Adam joined.
  17: FLAGS: 10000001 DATA: 12 bytes.
  18: ------------------------------
  19: 19:28:40:4245 [WebSocket #170] Server->Client (14 bytes)
  20: TYPE: TEXT.
  21: MESSAGE: Adam joined.
  22: FLAGS: 10000001 DATA: 12 bytes.
  23: ------------------------------
  24: 19:28:40:4275 [WebSocket #185] Server->Client (29 bytes)
  25: TYPE: TEXT.
  26: MESSAGE: Adam joined.
  27: Welcome Adam.
  28: FLAGS: 10000001 DATA: 12 bytes.
  29: ------------------------------
  30: 19:28:46:5764 [WebSocket #181] Client->Server (6 bytes)
  31: TYPE: PONG.
  32: MESSAGE: 
  33: FLAGS: 10001010 DATA: 0 bytes, masked using KEY: 16-39-8F-63.
  34: ------------------------------

On lines 1 and 7, the server sends a message to its then known clients: Ronald and John. When Adam joins on line 13, three messages are sent (lines 14, 19 and 24). On line 31 you see a control message: PONG that is sent as a keep-alive mechanism. I haven’t seen any PING messages in the wild yet.

 

Conclusion

Web sockets are a really nice extension for web development but you aren’t limited to a browser client as the only way to communicate to a server that supports the WebSocket protocol. In .NET 4.5 you have a nice set of client classes that enable any application to talk to a WebSocket server so if you need this kind of functionality, you now know where to look.

The current client classes do not (yet) support the entire protocol. The protocol defines extensions that a client can request from a server via the Sec-WebSocket-Extensions HTTP header field. There is no support for that in the current implementation.

And there is still something I’d like to test with web sockets on IIS8: how many clients are supported? Every client requires some server resources so how does the server handle this. Maybe something to investigate for a next blog post.

For the attached solution to work, you have to run the NuGet package manager first because I didn't include all packages in the zip file.

WebSockets.zip (480,87 kb)

Tags: , , , ,

Development

Walkthrough: Hosting FTP on IIS 7.5 in a Windows Azure VM

door Ronald 20-7-2012

I have been struggling yesterday and today to get FTP working on IIS 7.5 in a Windows Azure Virtual Machine and I just got it working. To remember all the steps myself and to help others in achieving the same, I’ll describe how to accomplish this.

 

Spin up a virtual machine

First of all, you need a virtual machine. I needed a Windows machine with a SQL Server database so I chose 'Microsoft SQL Server 2012 Evaluation Edition' from the available templates.

 

image

 

Once the machine has booted, you can RDP into it via the connect option at the bottom of the management portal.

 

image

 

When you’re in, you need to configure IIS. A summary of the required steps:

  • Add the 'Web Server (IIS)' role to the server.
  • Add the IIS features you need.
  • Add a TCP endpoint to your VM in the management portal with public and private port 80.

 

To enable FTP, make sure you enable the ‘FTP Server’ role services for your IIS role:

 

image

 

 

Add and configure FTP site

The next step is to create the actual FTP site in IIS. Right-click on ‘Sites’ in IIS Manager and select ‘Add FTP Site…’:

 

AddFTPSite

 

Specify the name and the local path for the site:

 

02 SiteInformation

 

Specify binding and SSL information:

 

03 Bindings

 

And finally specify who should have access to the FTP site. Note that I selected Basic Authentication and the administrator user. This corresponds to the local administrator account on the VM (the same account you use when you use RDP to login). This is definitely not the best solution. When you do not use SSL to secure access to the FTP site, your FTP credentials are sent in cleartext when logging in to the FTP site.

 

04 Auth

 

Local testing

You should now be able to access the FTP site from within the VM. Open a command prompt, type ‘ftp 127.0.0.1’ and login with your administrator account.

 

05 LocalTest

 

Well, that was the easy part. You now have an FTP site that you can access locally. When you try to access it from another machine, you will notice that you can’t get a connection.

 

We are now getting into the nitty gritty details of the FTP protocol. Whereas you may think that FTP only uses port 21, it actually doesn’t. I’m not going into the details but there’s a good explanation here.

 

Configuring remote connectivity

First of all, for active FTP, in theory you need to allow access to ports 21 (FTP command port) and 20 (FTP data port). So you need to add two endpoints to your VM:

 

06 FTP Active Endpoints

 

So far the theory. When attempting to connect to the FTP site using Filezilla, explicitly indicating that we’d like to use active mode, still no connection can be established. I haven’t been able to figure out why exactly…

 

But of course we can still try to configure passive FTP. For this to work, we need to tell the IIS FTP server the port range it can use for data connections and we need to add endpoints to the VM that correspond to this port range.

 

First of all, configure the port range and external IP address for passive data connections. This can be found in IIS Manager:

 

07 Firewall Support

 

08 Firewall Support

 

The external IP address should be the Virtual IP address you can find in the Azure Management portal. Unfortunately, it seems impossible to specify the data channel port range here. To set this, we need the appcmd utility, which can be found in ‘%windir%\system32\inetsrv’.

 

appcmd set config /section:system.ftpServer/firewallSupport \
/lowDataChannelPort:7000 /highDataChannelPort:7014

 

In the example, I chose ports 7000 to 7014 but you can choose any port range you like as long as it corresponds to the endpoints you configure for your Azure VM.

 

For configuring 15 extra endpoints for my VM I decided to use the Windows Azure Powershell cmdlets which you can download here. You can also add 15 endpoints in the management portal but you can only add them one by one which takes a considerable amount of time. To be able to use these cmdlets, you first need the publish settings file for your Azure account. There are a number of ways to download the publish settings file and one way is to start Windows Azure Powershell and use the cmdlet ‘Get-AzurePublishSettingsFile’. It opens a browser and allows you to download the publish settings file that corresponds to your Windows Live id.

 

When you have downloaded the publish settings file, you can import it using the ‘Import-AzurePublishSettingsFile’ cmdlet and we’re ready to start adding endpoints. I simply created a text file containing the list of commands I wanted to run and copied that into the Powershell window:

 

Get-AzureVM -ServiceName 'myServiceName' -Name 'ftpportal' \
| Add-AzureEndpoint -Name 'FTPPassive00' -Protocol 'TCP' \
-LocalPort 7000 -PublicPort 7000 \
| Update-AzureVM
Get-AzureVM -ServiceName 'myServiceName' -Name 'ftpportal' \
| Add-AzureEndpoint -Name 'FTPPassive01' -Protocol 'TCP' \
-LocalPort 7001 -PublicPort 7001 \
| Update-AzureVM
...

 

We are now almost there. Although the Windows firewall seems to allow all traffic that is required, you also need to enable stateful FTP filtering on the firewall:

 

netsh advfirewall set global StatefulFtp enable

Finally, restart the FTP Windows service and we should be up and running:

 

net stop ftpsvc
net start ftpsvc

Testing with Filezilla confirms that we can now successfully connect to our new FTP site, hosted on a Windows Azure VM:

 

09 FileZillaTest

 

Adding extra user accounts

As I said before, using the default administrator account for accessing your FTP site is a BAD idea because credentials are sent in clear-text. Therefore, create a new local user account on the VM and add an FTP authorization rule to allow access to your FTP site.

 

References

I had some help writing this article, mainly from this article by Angelo Laris that describes how to add active and passive FTP functionality to an Azure Web or Worker Role.
Other references include:

Tags: , ,

Development

Get more than 1500 members from an Active Directory group

door Ronald 5-1-2012

When retrieving the members of a group, Active Directory will never return more than 1000 (Win2000) or 1500 (Win2003) entries. This is true for all multi-valued properties. So suppose you have a group with more than 1500 members, it’s difficult to get all of them.

 

   1: DirectoryEntry groupEntry = ...;
   2: var members = groupEntry.Properties["member"];
   3: var nrMembers = members.Count;

 

In the example, nrMembers will never be larger than 1500, even when the group has more than 1500 members.

 

To overcome this problem, there are two possible solutions. First, instead of taking the group and reading the member property, look at all the potential members and check their memberOf property.

 

   1: // Construct a memberOf LDAP filter.
   2: var groupWithMembersDn =
   3:    "CN=TestGroup,OU=Groups,DC=itq,DC=local";
   4: var filter = String.Format("memberOf={0}", groupDn);
   5:  
   6: // Load only the objectSid of every member.
   7: var properties = new[] { "objectSid" };
   8:  
   9: // Get a root entry to start the search from.
  10: DirectoryEntry rootEntry = ...;
  11:  
  12: // Find sid's for all group members.
  13: var searcher = new DirectorySearcher(
  14:    rootEntry, filter, properties, SearchScope.Subtree);
  15: searcher.PageSize = 100;
  16: using (searcher)
  17: {
  18:    var memberResults = searcher.FindAll();
  19:    foreach (SearchResult memberResult in memberResults)
  20:    {
  21:       var memberSidBytes =
  22:          (byte[]) memberResult.Properties["objectSid"][0];
  23:       var memberSid = new SecurityIdentifier(sidBytes, 0);
  24:     }
  25: }

 

In the example we get the SID of every member of the group with distinguished name CN=TestGroup,OU=Groups,DC=itq,DC=local.

 

This approach has two disadvantages. One issue is that you always have to scope the search from a specific root entry. If you want to make sure you get every group member, your scope should always be the root of the Active Directory domain. This probably makes your search space too wide and therefore has a negative impact on performance.

 

A second and larger disadvantage is that this approach fails in a multi-domain or multi-forest environment. Since you scope the search to a specific root entry, you will find nothing outside this scope. So if the group has a member outside the current domain or forest, this member will not be found.

 

So, it would be nice if we could look directly at the group’s members without being bound by the 1500 entries limit. This can be accomplished via range retrieval of attribute values.

 

To use this, you first need to get the DirectoryEntry for the group you want to find the members of. This entry will be the search root of a DirectorySearcher. Next, you append a range option to the multi-valued property you want to load (in this case, the member property). Finally, you loop through each range and collect the member property values.

 

   1: var memberDns = new List<string>();
   2: const int increment = 999;
   3: var groupEntry = ...;
   4: int from = 0;
   5: while (true)
   6: {
   7:    // End of the range.
   8:    int to = from + increment - 1;
   9:  
  10:    // Attach a range option to the properties to load,
  11:    // for example: range=0-999.
  12:    var properties = new[]
  13:       { string.Format("member;range={0}-{1}", @from, to) };
  14:  
  15:    // Perform a search using the group entry as the base.
  16:    var filter = "(objectClass=*)";
  17:    var memberSearcher = new DirectorySearcher(
  18:       groupEntry, filter, properties, SearchScope.Base);
  19:    using (memberSearcher)
  20:    {
  21:       try
  22:       {
  23:          var memberResults = memberSearcher.FindAll();
  24:          foreach (SearchResult memberResult in memberResults)
  25:          {
  26:             var membersProperties = memberResult.Properties;
  27:             var membersPropertyNames =
  28:                membersProperties.PropertyNames
  29:                   .OfType<string>()
  30:                   .Where(n => n.StartsWith("member;"));
  31:             foreach (var propertyName in membersPropertyNames)
  32:             {
  33:                // Get all members from the ranged result.
  34:                var members = membersProperties[propertyName];
  35:                foreach (string memberDn in members)
  36:                {
  37:                   memberDns.Add(memberDn);
  38:                }
  39:             }
  40:          }
  41:       }
  42:       catch (DirectoryServicesCOMException)
  43:       {
  44:          // When the start of the range exceeds the number
  45:          // of available results, an exception is thrown
  46:          // and we exit the loop.
  47:          break;
  48:       }
  49:    }
  50:    // Increment for the next range.
  51:    from += increment;
  52: }

 

 

On lines 12 and 13 you can see the range option being added to the member property we wish to load. The from and to values of a range are inclusive so a range=0-4 contains the elements 0, 1, 2, 3, 4.

 

Line 24, where we iterate the found results, throws a DirectoryServicesCOMException when the from part of the range extends the number of elements to be found. This is one way to check whether we have found all results. Another way to check that we have arrived at the last result is described below.

 

The results contain member properties that are indexed as follows: for each result that is not the last result, the key to the actual members is member;range=0-999, member;range=1000-1999, etc. For the last result it is member;range=2000-* (even if we specify member;range=2000-2999 in the search). By checking that the returned property name ends with *, we can determine that it is the last result.

 

UPDATE: After some testing it appeared that my implementation had a bug. I wrote a while (true) {...} loop that was supposed to exit with an exception. Well, not always.... When the group has no members, the loop keeps running without ever exiting. So between lines 3 and 4 in the last example, you should check whether the group has any members and make sure never to enter the loop: if (groupEntry.Properties["member"].Count == 0) {...}. Alternatively, inside the loop, you could rewrite your end condition not to depend on an exception, but on the method described in the previous paragraph.

Tags: , ,

Development

Removing dependencies from a Windows service

door Ronald 18-8-2011

Provisior, one of the products we develop here at ITQ, has two components: an intranet website and a Windows service. We use the Windows service for long-running processes that you typically do not want to run inside a web server.

 

The Windows service is installed the first time you start the website. When it’s installed, we add a dependency to the NetLogon service. This prevents the Provisior service from starting before the NetLogon service. We did this because our service attempts to access a SQL Server database right when it starts using integrated authentication. This fails when the NetLogon service has not started yet.

 

All of this works fine when the machine that runs Provisior is joined to a domain. However, when that is not the case, the NetLogon service doesn’t run. You can start it, but it stops again immediately with the following error in the event log (at least on my machine):

 

This computer is configured as a member of a workgroup, not as a member of a domain. The Netlogon service does not need to run in this configuration.

 

So now I have a Windows service with a dependency on NetLogon that doesn’t run. How to fix this? Apparently, removing service dependencies is only possible from the Windows registry. You need to go to the following registry entry: HKLM\SYSTEM\CurrentControlSet\services\<service>. There you find a REG_MULTI_SZ key named DependOnService where you can remove any services your service depends on. After doing so I had to reboot my machine because the service control manager did not see my update.

 

I could have updated Provisior to install the NetLogon dependency only when the machine is joined to the domain (here is some info on detecting whether your machine is joined to a domain). However, Provisior is hardly ever run on a machine that is not joined to a domain. This only happens in testing scenario’s so I’d be changing code just for this very specific case.

Tags: , , ,

Development | Provisior

Prevent duplicate form submits in combination with jQuery client-side validation

door Ronald 8-5-2011

The title says it all: I want to prevent clients from posting the exact same data twice (or more) but I also want to use client-side validation. Let’s first explain why this is problematic.

 

Preventing duplicate form submits is easy. You disable the submit button in the onclick event handler:

<input type="submit"
       onclick="$(this).attr('disabled', 'disabled');" />

This works fine without client-side validation. But now suppose that some client-side validation rule has triggered and the form is invalid. You now have a disabled submit button on your form.

 

To fix this, we must make sure to only disable the button when the form is valid. We introduce a Javascript function to do this:

   1: function disableSubmitButton(b) {
   2:   // Disable submit button as soon as possible to prevent
   3:   // duplicate submits.
   4:   $(b).attr('disabled', 'disabled');
   5:   var valid = $(b.form).valid();
   6:   if (!valid) {
   7:     // Re-enable submit button when form is invalid.
   8:     $(b).removeAttr('disabled');
   9:   }
  10: }

And our button now looks like this:

<input type="submit"
       onclick="disableSubmitButton(this);" />

The valid() method is available from the jQuery Validation Plugin. It provides a lot of client-side validation options and can be used out-of-the-box with ASP.NET MVC3.

 

That was easy. When a user clicks the button, it is disabled immediately unless the form is invalid. However, this appears to work only in Internet Explorer. In Chrome and Safari (I haven’t tested Firefox), if the form is valid, it is no longer submitted. This has probably something to do with the fact that I define a custom onclick handler, preventing some default onclick handler from running. In Internet Explorer this problem doesn’t occur for some reason.

 

To fix this, I changed the disableSubmitButton function to the following:

   1: function disableSubmitButton(b) {
   2:   // Disable submit button as soon as possible to prevent
   3:   // duplicate submits.
   4:   $(b).attr('disabled', 'disabled');
   5:   var valid = $(b.form).valid();
   6:   if (valid) {
   7:     // Submit form when it is valid.
   8:     $(b.form).submit();
   9:   }
  10:   else {
  11:     // Re-enable submit button when form is invalid.
  12:     $(b).removeAttr('disabled');
  13:   }
  14:   // Always return false to prevent Internet Explorer from
  15:   // posting the form (which would then be the second post).
  16:   return false;
  17: }

The submit button is changed to:

<input type="submit"
       onclick="return disableSubmitButton(this);" />

A bit more work than I thought it should be but it works.

Tags:

Development

Using the FileHelpers library in a medium trust environment

door Ronald 6-5-2011

Yesterday I found out the hard way that you should never use a third-party library without doing some preliminary checks. First of all. always check the license: does the author allow you to use his library and if so, how exactly? Second, check whether the library allow partially trusted callers.

Many hosting providers allow you to run your web applications in medium trust instead of full trust. This means that code executing inside your application is not allowed to do a number of things like: opening files, accessing the registry, write to the event log or emitting dynamic classes using System.Reflection.Emit.

Yesterday I was deploying a website to a hosting provider that included the FileHelpers library. If you need to parse CSV files into something you can work with, this is the library for you. It parses CSV files into strongly-typed objects and provides a host of configuration options to do this. Obviously, it has to use some kind of reflection to set property values. At a minimum, it has to call PropertyInfo.SetPropertyValue somewhere. Fortunately, this call is not under any security restrictions.

However, probably for performance reasons, this is not how the FileHelpers library implements setting properties. Instead, it uses the System.Reflection.Emit namespace to build a dynamic class that is then used to set property values. The System.Reflection.Emit namespace is littered with methods that have the SecurityCritical attribute. These methods can therefore only be used in a full-trust environment. Which means that the FileHelpers library can not be used in my website...

The obvious question then is: couldn't you have checked this beforehand? Well, actually I could have. Assemblies that should allow calls from partially trusted callers must explicitly set the assembly-level attribute AllowPartiallyTrustedCallers. The FileHelpers library doesn't have this attribute. Therefore, the first call into the library generates an exception: System.Security.SecurityException: That assembly does not allow partially trusted callers.

This was a bit of a problem since I promised a customer that the website would be live today. There are probably better fixes but what I did was the following (only works for FileHelpers by the way). I downloaded the source code and included it into my solution. This still makes no difference because there are still the SecurityCritical attributes in the System.Reflection.Emit namespace. The only difference is that errors are now thrown a little later in the process but it is at least possible to call FileHelpers methods. Now I got a little lucky. FileHelpers has been around since the first version of the .NET framework when probably not all necessary functionality of System.Reflection.Emit was available to implement FileHelpers. Anyhow, the code contains some #if NET_1_1 directives that prevent the use of System.Reflection.Emit (instead, System.Reflection is used). So I simply set this compiler symbol and it all worked.

Tags: , ,

Development | Security

Confused by jQuery 1.6 .attr() and .prop()

door Ronald 4-5-2011

I just read the jQuery 1.6 release announcement and I must admit I'm a bit confused about the new .prop() method and the breaking change to the older .attr() method. In jQuery 1.6 a distinction is made between DOM attributes and DOM properties. Attributes are things like style, class and title and represent the state of the DOM as retrieved from the document. Properties are things like value, checked and disabled and represent dynamic state of the document.

For example, suppose an input element: <input id="myInput" type="checkbox" checked="checked" />.

If you called $('#myInput').attr('checked') in jQuery 1.5 you got back the value true. In jQuery 1.6 it would be "checked" (the actual value of the attribute). If you call $('#myInput').prop('checked') you get back true. So far so good although a lot of people will probably be annoyed by the breaking change to the .attr() method because there are thousands of lines of code that check the value of a checkbox by using .attr().

However, besides breaking .attr(), the announcement of this change is very confusing. If you want to set the checked attribute of a checkbox, I would expect you should use: $('myInput').prop('checked', true). But the announcement has the following line:

In jQuery 1.6 Boolean attributes (such as selected, checked, etc.) can now be toggled by passing in true or false to .attr() to either add or remove them.

This is inconsistent. If you call .attr('checked') you get back the value "checked" but you should set the attribute by passing in the value true. Or can you still call .attr('checked', 'checked') as you could in jQuery 1.5?

Tags:

Development

Comparing WIF claim sets from AppFabric Access Control Service V2

door Ronald 16-1-2011

Windows Identity Foundation or WIF is a Microsoft technology that allows you to move authentication and authorization logic out of your application. Suppose you are building a web site, then using WIF you no longer have to build your own login page and user store. Instead, you let a so-called Identity Provider, sometimes called a Security Token Service or STS authenticate your users. Your web application, called the Relying Party (RP), has a trust relationship with the STS and when the STS presents a user token to the RP, the RP knows that the user represented by the token is authenticated.

To clarify the example a little, suppose you choose Windows Live as your STS. When I hit a secure section of my web site, WIF intercepts the request and redirects it to the Windows Live authentication page. I sign in with my Windows Live credentials, Windows Live creates a security token for me and redirects me back to my web site using this security token. WIF checks the token and I’m authenticated.

Multiple identity providers

In the example above I used a single STS but suppose I want to allow users from multiple identity providers (Google, Yahoo, Facebook, etc). Microsoft has a solution for this called AppFabric Access Control Service V2, available on portal.appfabriclabs.com. It is a proxy between your application and currently five identity providers.

image

In the screenshot I have activated two identity providers.

Claim sets

When you sign in to an identity provider, it generates a token that represents who you are. So what exactly is a token? A token contains a set of claims about a user and is digitally signed by the STS. So when I login to Windows Live, a token is generated that contains some claims about me. A claim can be an e-mail address or a last name or anything else that the identity provider may wish to disclose about you. And that’s what we are interested in today. Each identity provider provides a different set of claims. Some may produce more claims, others may produce just an identifier. So the question is: what are the claims provided by each identity provider currently supported by AppFabric ACS?

For this post I have not yet tested Microsoft Active Directory Federation Services 2.0 because I haven’t set that up yet. When I have, I’ll update this post or write a new one describing the possibilities that ADFS offers.

Before we start it is important to note that AppFabric ACS simply passes all claims from each identity provider through unchanged.

Windows Live ID

Windows Live is the provider that offers the least amount of claims. It only gives your application a unique user identifier. The other claim is added by AppFabric ACS to let you know what identity provider was used (you’ll see this claim type for every STS).

WindowsLive

Windows Live does not offer any options for getting more user information. This is a deliberate choice to protect user’s privacy. This is also the reason that Windows Live does not ask the user for confirmation to share information with a third-party application: no personal information is actually shared.

Google

Google offers a little more user information when asked for it. Besides, Google as an additional step explicitly asks your permission to share your information with AppFabric ACS. In my case this means that I have to confirm that rwwilden-appfabric-labs.accesscontrol.appfabriclabs.com is allowed access to information from my Google account.

Google

The reason Google asks for your permission to share information is that they actually provide information that can be traced back to a person.

Google does not offer any options for getting additional information.

Yahoo!

Yahoo! provides the same claims Google does. They also have a confirmation step to allow the user to think again before sharing information with a third-party application.

Yahoo!

Facebook

Facebook does not have a very good record of keeping its user’s data private (I inserted some random links to sites I found when searching for ‘facebook privacy violation’). However, the set of claims when using Facebook as your STS is limited. The extra claims are an access token and an expiration date.

Facebook

Besides, Facebook asks for your permission when you sign in for the first time, just as Google and Yahoo! do.

However, you can configure AppFabric ACS to ask the Facebook STS to grant access to an extensive set of permissions based on the access token claim. For example, I can let AppFabric ACS ask Facebook for permission to read a user’s birthday.

FacebookAppFabricACS

The above screenshot is from the AppFabric ACS portal. When I sign in this time via Facebook I get a new request for permission (in Dutch) asking me to allow the third-party application access to the birthday field.

image

When I allow this, there are no new claims added to the claim set. However, I can use the provided access token to get additional user data via Facebook’s Graph API. The url

https://graph.facebook.com/rwwilden?fields=birthday&access_token=

gives me a small JSON document:

{
   "birthday": "04/11/1977",
   "id": "100001960422926"
}

containing my birthday. So far so good. My application has requested permission to access my birthday, I have given this permission and using the provided access token claim I can access the birthday field.

What about other fields? The documentation for the Facebook API User object specifies that I need the user_work_history permission to read the work field. I have never given this permission so the following url should generate an error or at least no work history.

https://graph.facebook.com/rwwilden?fields=work&access_token=…

And it works as expected:

{
   "id": "100001960422926"
}

 

Conclusion

The differences between identity providers currently supported by AppFabric ACS are large. On the one end there is Microsoft with Windows Live, providing only a user identifier. On the other end there is Facebook with a lot of configuration options. If you want to support Windows Live ID, the only information the STS gives you is that a user is authenticated. Additional user information can only be stored inside your application.

Even if you use Google or Yahoo! you probably need to store additional user information. The Facebook API offers all personal information you may ever need to know about your users so there is no need to store any additional user information inside your application. Facebook clearly wins when you look at it from an application builder standpoint.

From a privacy standpoint it is clear that Microsoft wins. For a third-party application making use of Facebook as an STS it is very easy to know all about its users. It’s only one button click away.

Tags: , , , ,

Cloud | Development | Informatie beveiliging | Security

Using the ASP.NET MVC 3 Razor view engine in Windows Azure

door Ronald 11-1-2011

Yesterday I was trying to upgrade an ASP.NET MVC 2 web application to ASP.NET MVC 3 RC2. I also wanted to use the new Razor view engine that allows a more compact and less obtrusive way of writing ASP.NET MVC view templates.

There already is an excellent guide of how to upgrade your MVC 2 application to MVC 3 RC2. If you are running your MVC 2 application in Windows Azure, you must take some additional steps, which are described here. Visual Studio 2010 doesn’t (yet) allow you to specify an ASP.NET MVC 3 Web Role, so when running in Azure, you always have to upgrade if you want to use MVC 3.

You should reference some extra DLL’s from your MVC project and make sure that for all these DLL’s you set Copy Local to True. This is necessary because on the virtual machine your application is deployed to these DLL’s are not in the GAC. The DLL’s you have to reference are:

  • Microsoft.Web.Infrastructure
  • System.Web.Helpers
  • System.Web.Mvc
  • System.Web.Razor
  • System.Web.WebPages
  • System.Web.WebPages.Razor
  • WebMatrix.Data (I’m not sure why this one is necessary)

and these are located either somewhere below C:\Program Files (x86)\Microsoft ASP.NET for 64bits or below C:\Program Files\Microsoft ASP.NET for 32bits. So far so good.

Having taken all the required steps (or so I thought) I tried to run my first Razor template in the Windows Azure emulator:

image

No what I expected: The name ‘ViewBag’ does not exist in the current context. I must have forgotten something. After double-checking I had all the required dependencies that all had there Copy Local property set to True, I took a better look at the Compilation Source. My page appeared to extend from System.Web.WebPages.WebPage. This class doesn’t have a ViewBag property so that explains the compilation error.

Back to the upgrade instructions. It appeared I had forgotten one small step. In the Views sub directory there is an additional Web.config file. In an MVC 3 project, it contains a new configuration section: system.web.webPages.razor. In this section, a new page base type is specified: System.Web.Mvc.WebViewPage. After copying this Web.config file from another MVC 3 project, everything worked as expected.

A classic case of RTFM. But if you ever happen to see this compiler error when using the Razor view engine, check your Web.config file.

Tags:

Cloud | Development | ASP.NET MVC

Windows Azure Extra Small VMs

door Ronald 16-12-2010

At PDC10 Microsoft announced a new Compute Instance Size: Extra Small. Previously, there were four Azure Virtual Machine Instance sizes: Small, Medium, Large and Extra Large, each with its own price and resources. The standard price for an Extra Small instance is $0,05/hour (to compare, the Small VM costs $0,12/hour). What you get for this price is not a lot as you can expect:

 

  • 1 x 1GHz processor
  • 768 MB memory
  • 20GB storage
  • 5Mbps network bandwidth

 

These low resource levels are achieved by having multiple XS VMs share resources on the same node.

 

In my opinion the new XS VMs are interesting for two reasons. First of all, they’re ideal for testing purposes. Individual developers can get there own Windows Azure account and use it to deploy only XS instances. A few days ago I deployed an ASP.NET MVC app to four XS instances. After confirming everything worked, I removed the hosted service and the whole excercise cost me just $0,20.

 

A second reason this may be interesting is when you have a large workload that you can divide over a (very) large number of nodes. Suppose you have a workload that can be divided into 1000 chunks. Using 1000 Extra Small instances will cost you $50,00 per hour, using the same number of Small instances will cost you $120,00 per hour.

 

Deploying Windows Azure roles to Extra Small VMs is really easy. Just like the other VM sizes you select it in the role’s property panel:

 

image

Tags: ,

Cloud | Development