anandj123′s Blog

April 24, 2009

How to do WCF UserName token authentication with dynamic proxy for test driven development (TDD)

Filed under: Uncategorized — anandj123 @ 2:52 pm

Problem definition:

Previously I have linked to a website which describes in detail how to enable your WCF applications for username tokens authentication. That article went thru the setup of username token authentication using configuration files and client proxies. That is alright if you want to generate client proxies and use in your client application.

For testability reasons (which I will describe) we need to be able to use dynamic proxies for WCF services. I had to search a few places to gather all the required information to do this. Here I will describe how it can be done from a test driven development (TDD) methodology standpoint.

Approach/Solution:

In the example I have used a classical MVC pattern with service implementation for retrieving the model. At a high level the complete data flow is represented as:

 

I am just going to explain about the Web-server layer where the MVC pattern and WCF dynamic proxy resides. Maybe later I can go explain the other tiers and some of their nice features.

I created a View like the following:

public interface ISearchUserView
{
string SystemTextBox {get; set;}
string HostTextBox { get; set; }
string BankTextBox { get; set; }
string PathTextBox { get; set; }
ArrayList DataFormatDropDownList { get; set; }
ArrayList TradingMethodDropDownList { get; set; }
GenericCollection<UserData> UserGridView { set; }
}

 The implementation of the view is not important for this article, so I will leave that as well for now.

Then I created a controller which holds the view.

public class SearchUserController
{
 
private IDataService _service = null;
private Views.ISearchUserView _view = null;
public Views.ISearchUserView View
{
get { return _view; }
set { _view = value; }
}

 

Now the relevant parts are the following code:


public SearchUserController(Views.ISearchUserView view)
{
View = view;
 

WSHttpBinding binding = new WSHttpBinding(“WSHttpBinding_IDataService”);
EndpointAddress address = new EndpointAddress (new Uri(“http://localhost:8731/Web.Service/DataService/”),
new DnsEndpointIdentity(“MyServerCert”));

ChannelFactory<IDataService> service = 
new ChannelFactory<IDataService>(binding, address);
 
// Add the user name token to the request, these should come from the config file

// where we should encrypt them

service.Credentials.UserName.UserName = “test”;
service.Credentials.UserName.Password = “test”;
 
// Custom SSL validation of the server certificate,

// this we don’t need in production.

// in dev the cert is really not valid so to bypass the certificate validation

// we need custom validation here.

service.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.Custom;
service.Credentials.ServiceCertificate.Authentication.CustomCertificateValidator = new MyX509Validator();
 
_service = service.CreateChannel();
}
 
// Dependency injection
public SearchUserController(IDataService service, Views.ISearchUserView view)
{
_service = service;
_view = view;
}

 Let me go thru each line of the code.

public SearchUserController(Views.ISearchUserView view)

This is a constructor that I am overriding which accepts a view, nothing special.

WSHttpBinding binding = new
WSHttpBinding(“WSHttpBinding_IDataService”);

Now we need to define the binding properties in the client side to connect to the server. The server is an implementation of the following ServiceContract.

[ServiceContract]
public interface IDataService
{
[OperationContract]
GenericCollection<UserData> GetUsers(string bankName);
}

 The binding is defined in the web.config file of the web-project as:

<!– WCF binding configurations –>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name=WSHttpBinding_IDataServicecloseTimeout=00:01:00
openTimeout=00:01:00receiveTimeout=00:10:00sendTimeout=00:01:00
bypassProxyOnLocal=falsetransactionFlow=falsehostNameComparisonMode=StrongWildcard
maxBufferPoolSize=524288maxReceivedMessageSize=65536
messageEncoding=TexttextEncoding=utf-8useDefaultWebProxy=true
allowCookies=false>
                    <readerQuotas maxDepth=32maxStringContentLength=8192maxArrayLength=16384
maxBytesPerRead=4096maxNameTableCharCount=16384 />
                    <reliableSession ordered=trueinactivityTimeout=00:10:00
enabled=false />
                    <security mode=Message>
                        <transport clientCredentialType=BasicproxyCredentialType=Nonerealm=“” />
                        <message clientCredentialType=UserNamenegotiateServiceCredential=truealgorithmSuite=DefaultestablishSecurityContext=true />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
    </system.serviceModel>

 Most of the configuration entries are not important at this point. Only thing that is worth pointing out is the security settings.

                    <security mode=Message>

                        <transport clientCredentialType=BasicproxyCredentialType=Nonerealm=“” />

                        <message clientCredentialType=UserName“  negotiateServiceCredential=truealgorithmSuite=DefaultestablishSecurityContext=true />

                    </security>

 Basically we are stating that we will use message level security with credential type UserName. To use this setting we need to have SSL connection between client and server. That is a necessary requirement because we will be sending the username and password across the wire in clear text.

EndpointAddress address = new
EndpointAddress(new Uri(“http://localhost:8731/Web.Service/DataService/”),

 The above code is just setting the server host address, nothing special here.


ChannelFactory<IDataService> service = new ChannelFactory<IDataService>(binding, address);

 Here I am creating the ChannelFactory for the particular service implementation.

service.Credentials.UserName.UserName = “test”;

service.Credentials.UserName.Password = “test”;

 The above 2 statements is where we need to set the user name and password for the UserName token authentication. Generally these values should be stored in a configuration file or something, but for demonstration purpose I hard-coded them.

service.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.Custom;

service.Credentials.ServiceCertificate.Authentication.CustomCertificateValidator = new MyX509Validator();

 _service = service.CreateChannel();

Now the above 2 lines are not terribly important. In a production environment the server will have a valid X509 certificate from a certificate authority such as GeoTrust® but for development environment I did not have a certificate so I created a dummy certificate using the following command

makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=MyServerCert -sky exchange –pe

That means it’s not a valid certificate. So if you want to bypass the actual server certificate validation (for SSL communication) then you can use the above logic to short circuit the validation.

The code for MyX509Validator is pretty straightforward for demonstration purpose:

public class MyX509Validator : X509CertificateValidator
{
public override void Validate(X509Certificate2 certificate)
{
// validate argument
if (certificate == null)
throw new ArgumentNullException(“certificate”);
 

// check if the name of the certifcate matches
if (certificate.SubjectName.Name != “CN=MyServerCert”)
throw new SecurityTokenValidationException(“Certificated was not issued by thrusted issuer”);
}
}

 That is all you need to create a dynamic proxy with full SSL/UserName token validation. You may ask, why do I need this? I can achieve the same result using configuration files as well? Well as I mentioned this is important for TDD. Here is the testability code:

Dependency Injection:

// Dependency injection

public SearchUserController(IDataService service, Views.ISearchUserView view)

{

_service = service;

_view = view;

}

The visual Studio Test code is as follows:

[TestMethod]

public void TestPortalUserDataControllerIscallingTheServiceAndSettngValueIntoTheView()
{
MockObjects.MockISearchUserView view = new MockObjects.MockISearchUserView();
MockObjects.MockIDataService service = new MockObjects.MockIDataService();
SearchUserController controller = new
SearchUserController(service, view);
 
controller.SearchButton_Click();

Assert.AreEqual(“Test Bank”, view._data[0].Bank);
}

 Pretty straight forward. We need to create 2 mock objects 1 for the view and another for the WCF service. Inject them to the Controller and voila. Here are the mock objects (I did not bother to use any mock framework here, but you can use one).


public class MockISearchUserView : ISearchUserView
{
public string BankTextBox
{
get
{
return “Test”;
}
set
{
throw new NotImplementedException();
}
}
}

 And

public class MockIDataService : IDataService
{
#region IPortalDataService Members
 

public GenericCollection<UserData> GetUsers(string bankName)
{
GenericCollection<UserData> users = new GenericCollection<UserData>();
 
UserData user = new
UserData();
user.Bank = “Test Bank”;
users.Add(user);
 
return users;
}
 
#endregion
}

 Maybe in a future post I can go into detail about the testing things.

Conclusion:

Here I wanted to demonstrate how you can use the WCF authentication mechanism without generating client proxies. You can use the dynamic proxy functionality provided in WCF to achieve the same results. This is important for the testability reasons as I demonstrated. TDD is a way of coding, if you like it there is no greater thing that happened to software development that this. If you don’t like it, you will hate it for all the complexity that it brings in. I am in the former camp.

3 Comments »

  1. Super interesting post! Truely!

    Comment by Mara Diehl — May 28, 2010 @ 7:57 pm | Reply

  2. Hehe I am literally the only comment to this incredible read?!?

    Comment by Taylor Stapleton — May 30, 2010 @ 5:10 pm | Reply

  3. If only more than 12 people would hear about this..

    Comment by Erwin Villanueva — May 31, 2010 @ 7:29 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.