Friday, 9 March 2012

Validation in web services

A colleague recently informed me that doing validation in a web service was a bad idea and should be avoided. I think his reasoning was that in systems requiring massive throughput performing validation at a web service entry point would be a blocking operation that would hold resources and impact negatively on overall system performance. Sorry, but the idea of not validating a request to a web service struck me as stupid. Anyway, I like to get my ducks in a row so I thought I’d take the opportunity to revisit some SOA concerns around validation.

My usual source of reference on matters pertaining to SOA is Thomas Erl. So, I dug out my copy of “SOA – Principles of Service Design” and had a look at what it had to say on the matter.

“Regardless of the extent of indirect coupling a service contract imposes, there will always be the requirement for the consumer program to comply to the data model defined in the technical service contract definitions.

In the case of a Web service, this form of validation coupling refers to the XML schema complex types that represent individual incoming and outgoing messages. Schemas establish data types, constraints, and validation rules based on the size and complexity of the information being exchanged as well as the validation requirements of the service itself.” [1]

So, in the case of a SOAP-based XML Web service the schema specifies the nature and format of the data contained in the request. A Web service will perform validation against the schema and reject the request if it does not comply. Erl goes on to say,

“The extent of validation coupling required by each individual service capability can vary dramatically and is often tied directly to the measure of constraint granularity of service capabilities. As per the validation-related design patterns, each contract can be individually assessed as to the quantity of actual constraints required to increase its longevity.” [2]

When Erl refers to the constraint granularity he is talking about “the amount of detail which a particular constraint is expressed” [3]. Erl provides the example of a constraint to be applied to a product code; you could mandate that it is a string value between 1 and 4 characters (a course grained constraint) or that it is 4 characters with each character being numeric (a fine grained constraint).

Erl’s concern about service longevity appears to be that if constraint granularity is high so is the validation coupling. Changes to validation rules could pose a threat to service longevity because they would require a change to the service contract. As Erl puts it, “By reducing the overall quantity of constraints and especially filtering out those more prone to change, the longevity of a service contract can be extended.” [4]

Erl offers the Validation Abstraction pattern to help alleviate this issue.

So, does all of this mean that performing validation in a Web service is a bad idea. No, of course it doesn’t. What this means is that by reducing the validation detail within the schema can help increase the longevity of service contracts.

However, if you do this you must defer more detailed validation to the underlying service logic, not ignore it altogether. It is worthy of note that some validation logic may be better suited to being performed within the processing boundary of a business service (i.e. moving the validation logic closer to where specific business processing takes place). Use of a dedicated validation service is also an option so validation logic can be maintained on a single location and managed separately.

What I take away from this:

  1. Validation rules are expressed for service contracts using schema.
  2. Keeping validation constraints course grained can help extend service contract longevity.
  3. Consideration should be given to move detailed validation logic into the underlying service logic.
  4. Moving some validation logic within the business process boundary should be considered.
  5. Use of an external validation service is an option.

As for my colleague’s original assertion, I think I’ll stick to performing validation in my Web services as and when required.

References

[1] “SOA – Principles of Service Design”, Thomas Erl, pp190-191

[2] “SOA – Principles of Service Design”, Thomas Erl, p191

[3] “SOA – Principles of Service Design”, Thomas Erl, p117

[4] http://www.soapatterns.org/validation_abstraction.php

 

See also

http://searchsoa.techtarget.com/answer/Validation-abstraction

Friday, 9 March 2012

Login failure when accessing Active Directory

I hate wasting time, especially when it’s because of an obvious error that’s staring me in the face but I just can’t see it. Well, it happened today so I thought I’d make a note so it never happens again. Take the following code:

var principalContext = new PrincipalContext(ContextType.Domain, domain);
var validated = principalContext.ValidateCredentials(userName, password);
var userPrincipal = UserPrincipal.FindByIdentity(principalContext, userName);

My problem was that an exception was being thrown on the last line. “How is this possible?” I asked myself. “The call to ValidateCredentials worked so why the error on FindByIdentity?”

Firstly, the exception:

Exception thrown autenticating the user.
System.DirectoryServices.DirectoryServicesCOMException (0x8007052E): Logon failure: unknown user name or bad password.

   at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
   at System.DirectoryServices.DirectoryEntry.Bind()
   at System.DirectoryServices.DirectoryEntry.get_AdsObject()
   at System.DirectoryServices.PropertyValueCollection.PopulateList()
   at System.DirectoryServices.PropertyValueCollection..ctor(DirectoryEntry entry, String propertyName)
   at System.DirectoryServices.PropertyCollection.get_Item(String propertyName)
   at System.DirectoryServices.AccountManagement.PrincipalContext.DoLDAPDirectoryInitNoContainer()
   at System.DirectoryServices.AccountManagement.PrincipalContext.DoDomainInit()
   at System.DirectoryServices.AccountManagement.PrincipalContext.Initialize()
   at System.DirectoryServices.AccountManagement.PrincipalContext.get_QueryCtx()
   at System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithTypeHelper(PrincipalContext context, Type principalType, Nullable`1 identityType, String identityValue, DateTime refDate)
   at System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithType(PrincipalContext context, Type principalType, String identityValue)
   at System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(PrincipalContext context, String identityValue)
   at GL.AnglianWater.WIRM.Core.Membership.LdapAuthentication.Authenticate(String userName, String password, String domain, String requiredGroup)

Drilling into the exception details in Visual Studio 2010 yielded a bit more information:

ExtendedError = -2146893044
ExtendedErrorMessage = "8009030C: LdapErr: DSID-0C0904DC, comment: AcceptSecurityContext error, data 52e, v1db1"

After scratching my head for an age the solution turned out to be so obvious as to be embarrassing; simply create the PrincipleContext by passing in the username and password too:

var principalContext = new PrincipalContext(ContextType.Domain, domain, userName, password);
var validated = principalContext.ValidateCredentials(userName, password);
var userPrincipal = UserPrincipal.FindByIdentity(principalContext, userName);

Doh!

Sunday, 4 March 2012

XML schema for log4Net

Here’s a quick reminder about the XML schema for log4Net. I use this if I choose to put the log4Net configuration in a separate file and want Visual Studio intellisense to work.
Firstly, to get the log4Net configuration into a separate file I do something like this in the application configuration file (e.g. App.config):
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
  </configSections>

  <log4net configSource="Config\Log4Net.config"/>
</configuration>
In the Log4Net.config file I make sure the log4Net element is given a reference to the log4Net schema and and a namespace:
<log4net xsi:noNamespaceSchemaLocation="http://csharptest.net/downloads/schema/log4net.xsd"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  
  <!-- log4Net configuration omitted -->

</log4net>
Hey presto! We now have intellisense for the log4Net configuration file.

Thursday, 1 March 2012

Specifying x86 at the solution level and in CruiseControl.Net

On my latest project I have to ensure all the projects in a solution are built using the x86 platform. To ensure this works as expected I needed to use the Configuration Manager in Visual Studio 2010. I also needed to ensure the solution built correctly in CruiseControl.Net.

Visual Studio Configuration Manager

The configuration manager can be found is 2 ways:

  1. Go to Build > Configuration Manager…
  2. Right-click on the solution in the Solution Explorer and select Properties from the context menu. Click the Configuration Manager… button in the solution property pages dialog.

Once the configuration manager has opened you can change the build configuration for the entire solution. Here’s an example before making changes to the build configuration:

Untitled

You can now select the appropriate build configuration and platform for each project in the solution. If you change the Active solution platform you may find that the Build check boxes are all unticked. Don’t forget to tick them if you want the projects to build (note that you can get caught out here; if these check boxes are cleared and you subsequently try to Clean or Build at the solution level nothing will happen).

 Untitled1

Sometimes the x86 option is not available in the Platform dropdown next to each project. To create it select the <New…> option and in the resulting New Project Platform dialog box do the following:

  1. Select x86 under New platform:
  2. Under Copy setting from: choose Any CPU
  3. Deselect Create new solution platforms
  4. Click OK

Untitled2

CruiseControl.Net configuration

To modify the CruiseControl.Net project configuration change the MsBuild task as follows:

<msbuild>
    <executable>C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe</executable>
    <workingDirectory>C:\<your>\<working>\<directory></workingDirectory>
    <projectFile>Your.Solution.File.Name.sln</projectFile>
    <timeout>600</timeout>
    <buildArgs>/p:Configuration=Release /p:Platform=x86</buildArgs>
    <logger>ThoughtWorks.CruiseControl.MsBuild.XmlLogger,C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>

Note line 6 where we specify the configuration and the platform.