Monday, June 16, 2008

Customize web.config Sections and Compile from Command Line


It is always important to being able to customize a software project build steps. Always there is the need to support at least two configurations: Debug and Release, but for some other projects you might need more configuration types. For instance you might need to support more than one database username depending on the data schema forcing you to come up with a different connectionStrings section in the web.config file depending on the username being used, you could also need to compile the XML documentation using a tool like SandCastle and come up with an automated MSDN-like help site or any other documentation style, but you might not want to do that always since the those documentation tools’ build process takes a bit of extra time to execute.
The project file (.csproj) is compiled by default using msbuild which is shipped by default with .NET 2.0 and higher and with the latest version of Visual Studio. This project file is nothing more than an XML file that defines the steps and properties msbuild needs to follow in order to come up with the correct build, something like an Ant or NAnt file. One very sensitive part of a build is the ability to replace web.config sections, such as the connectionStrings section. The guys at Microsoft provide the Web Project Deployment tool that implements a task called webConfigReplace, all this task does is pretty simple: it takes a file containing the XML that will replace the existing section in web.config. This works great for ASP .NET projects with the added dependency of an extra project file in the solution, but what if you want to get rid of the extra project in the solution and implement the replacement task in the actual project file (.csproj) containing your project? It’s actually not that complicated, all is needed is to download the Web Project Deployment tool and include the library in the project file as seen here:


<project toolsversion="3.5" defaulttargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"><!-- Import WebDeployment.Tasks.dll v9.0 in order to utilize the ReplaceConfigSections task to customize the web.config file --><usingtask taskname="ReplaceConfigSections" assemblyfile="$(MSBuildExtensionsPath)\\Microsoft\\WebDeployment\\v9.0\\Microsoft.WebDeployment.Tasks.dll">

Then it is just a matter of setting the properties for this tool and that’s about it now you can come up with a new web.config file every time you hit compile:

<ItemGroup>
<WebConfigReplacementFiles Include="connectionStrings\connectionStrings.config">
<Section>connectionStrings</Section>
</WebConfigReplacementFiles>
</ItemGroup>
<ReplaceConfigSections RootPath="$(OutputDir)$(Configuration)\" WebConfigReplacementFiles="@(WebConfigReplacementFiles)" UseExternalConfigSource="false" ValidateSectionElements="false" />

Another cool thing is that you can compile your project from a command line which will get rid of the Visual Studio dependency to build, also you can change the value of properties inside the project file with the /p command line argument to msbuild. This method also allows organizations that are working on a mixed environment (unix/Windows, java/.net) to understand each other since msbuild looks a lot more like a tool that can be controlled from command line and XML project files which the java guys are used to, this integration of the teams gives more power and value to the organization since it helps break those mental barriers that java and .net developers often have.

Thursday, June 05, 2008

ADAM and AzMan Nightmares?

In a recent project there was a need to work with active directory users as well as external users that would register to an application. Each type of user would have different permission levels into the app’s data. The security expert told me that I wasn’t allowed to make direct calls to AD from the app which is sitting on the DMZ, that requirement killed the easy approach to just make a direct call to AD and authenticate.

After some searching I found ADAM which stands for Active Directory Application Mode. If you read all ADAM’s documentation, you will find out that ADAM allows maintaining a set of local ADAM users and by using methods such as Windows pass through authentication or AD proxy users you can authenticate AD users via ADAM, basically making ADAM the complete authentication service for an application. The security requirements asked for ADAM to be on the DMZ and to have ADAM making secure simple LDAP calls to the internal AD servers. I thought I could accomplish that by having ADAM deployed on a machine sitting on a specialized DMZ without joining it to the domain, but we were proved wrong. We talked to Microsoft for hours (or days!) on the phone and over newsgroups until one of the guys that helped us at Microsoft finally talked to the correct people and found out that there is no way ADAM could authenticate AD users without having it’s host Windows server being part of the domain. That took us a lot of time to figure out, now I had to convince the security guy that the only solution to the requirement was to have ADAM’s server joined to the domain. We were able to configure ADAM so that on one side it receives the app server’s requests via simple LDAP calls and on the other side it makes the thick secure domain AD queries to authenticate all internal users. Also, we setup ADAM to work using SSL and now we had a secure communication channel from the app server to ADAM and the regular secure AD calls from ADAM to the domain controllers. The security guy wasn’t all happy about the solution but we thought it was the best we could do with ADAM. The next problem to tackle has a name: userProxy.

The method we chose to maintain an AD pointer for internal users was the user proxy method. This method uses a synchronization setting file that synchronizes any number of AD users with ADAM. The problem with the synchronized users in ADAM is that the users’ class when in ADAM is userProxy, this type of user class is not supported by the default ASP .NET MembershipProvider and in order to find out I had lots of communication with Microsoft again and with other AD experts over forums. Then I saw this article that confirmed the theory that the userProxy is not supported by the provider. The only solution left to do was to create our own custom MembershipProvider which I did and by the way I made it a bit more flexible in terms of the LDAP queries it can handle. Now I had an app that was able to make secure LDAP queries using the provider pattern and ADAM as a middle tier to securely connect to AD, all of the ADAM users were supported naturally by ADAM with no special change in the provider code, this was cool and it was putting the app in a better shape to support both types of users. The only problem is that so far we could only authenticate, we needed to maintain different levels of authorization depending on the type of user, so here we go again, the next problem was authorization and specifically the problem had a name: AzMan.
AzMan is a product that helps maintain roles, tasks and operations for any application that might need them, basically manages the authorization part. AzMan can store roles on an XML file or can get hooked to an AD product like ADAM. Naturally you would think that since you have authentication in place using ADAM, AzMan should just use ADAM as it’s store, well we were proven wrong again. After more hours (days?) of endless calls to Microsoft we hit the right guys and they finally pointed out that AzMan requires all servers accessing it to be part of the domain which completely breaks our current implementation of having the app server detached from the domain and only the server hosting ADAM being part of the domain, authorization with AzMan had to be discarded. The quick solution to that was easy, use the database server that was serving the app’s data to maintain the Database Role provider data as well, we used the Oracle Role Provider for .NET between the DBA and myself we were able to set it up in hours, that solved all problems and the security code of the app didn’t need to be changed thanks to the provider model. A good thing is that no Role provider code was needed since Oracle provides the implementation for free.

The lessons learned with this experience are:
  • In order to support internal AD users ADAM must be part of the domain or a more complex solution must be implemented such as ADFS.

  • App servers can use ADAM and being detached from the domain if they are facing the internet.

  • Always setup ADAM to use SSL in order to maintain a secure data flow.

  • AzMan = Domain, you are in a much better place by just using the regular Roles DB providers.
I personally think that ADAM is a great product but Microsoft should open it more so that LDAP simple calls to internal ADs can be made in order to accommodate possible security requirements. Security guys don’t like the amount of communication a server that is part of a domain needs to have with its domain controller. I think that userProxy queries should be part of the MembershipProvider or we will end up with hundreds of MembershipProviders throughout the world. AzMan is a product that is made to work on internal apps only, It looks great, but again it needs that thick level of communication with domain controllers.
If you ever need more details on the implementation I made for this project do not hesitate to comment on this blog.