I see various questions on configuration difficulties in using Workitem OM in web application. Unfortunately workitem object model is not yet optimized for web scenarios and it still uses a cache per user. So implementing security gets tricky. I wrote below document a while back and Pete also posted it as attachment for me, but am posting here in blog so that it comes up in search engine for those looking for answer.

 

See here for an example of using WIT OM in Asp.net as datasource: http://blogs.msdn.com/kannans/archive/2007/04/23/tfs-data-source-asp-net-control.aspx and http://blogs.msdn.com/kannans/archive/2007/04/23/tfs-reporting-simplified.aspx 

 

How to configure WIT OM on a web application?

 

Summary:

This document describes how to configure web applications to use work item tracking OM. This document assumes the web application is created in Asp.Net using IIS 6.

 

1)      Decide on physical architecture:

 

Possibilities are:

-          Web Application (WebApp) and Application Tier (AT) are in same machine

-          WebApp and AT are in different machine.

 

WIT OM needs to run under connecting user’s credentials so it needs to run under impersonation. If WebApp and AT are in same box then impersonation will work fine without additional configuration.

 

If WebApp and AT in different boxes, then it can be implemented using two options below:

 

-          Using Constrained Delegation: Both WebApp and AT connection can work under impersonated account if constrained delegation is enabled over AT’s port 80 on soap over http. See article in this link for details on constrained delegation and setup steps.

-          Using OWA like Forms authentication: Similar to OWA, user’s credentials can be obtained using a web form and used to connect to WIT OM and AT. To implement this, check the sample code on appendix and configure the server in same way as WebApp being on AT.

 

See here for an easy-to-understand article on delegation and impersonation: http://pluralsight.com/wiki/default.aspx/Keith.GuideBook/WhatIsDelegation.html

 

2)      Enable Integration Authentication

 

Set the directory security for the Web application to Integrated Windows authentication and turn off anonymous access. For step-by-step details, check the appendix below.

 

3)      Configure WIT Cache folder

 

To use WIT OM on a web site, you need to configure WebApp so that it uses the shared administrative data cache directory. You should configure WIT-OM to use this directory because impersonated connecting users do not normally have their user profile loaded. OM places the administrative data cache in the USERPROFILE\Local Settings\Application Data\Microsoft\Team Foundation\1.0\Cache directory by default. Another reason to configure PSOM to use the shared administrative cache directory is to avoid the waste of disk space and virtual memory space that occurs when you cache the same administrative data for each connecting user.

 

Create a folder in WebApp machine. For the web application’s web.config file, add key WorkItemTrackingCacheRoot with value of cache folder in appSettings. Example below:

 

<configuration>

   

    <appSettings>

        <add key="WorkItemTrackingCacheRoot" value="E:\FolderForCache" />

    </appSettings>

</configuration>

 

4)      Enable ASP.NET impersonation

 

Skip this step if you are using Forms authentication. In order to use connecting user’s context, the call to the WIT AT needs to happen on a thread that is impersonating the web client.

 

Add the following section to web.config file of your web application:

 

<identity impersonate="true" ></identity>

 

Another option is to change .aspx pages to ASP compatibility mode. This will force ASP.NET to initialize its worker threads in a apartment threading model. This will make WIT OM to execute in the same thread as the thread that impersonates the web client:

<%@ Page aspcompat=true ….

 

5)      Create profile for service account if using Forms Authentication

 

If you are using Form’s Authentication to get user’s credentials using an OWA like form, then the service account that the webapp runs under needs to have a profile. This is needed to write registry entries. Login once into the WebApp box using service account.

 

6)      Enable read/write access to Temporary ASP.NET files for Authenticated Users

 

If impersonation is used, ASP.NET is going to impersonate the clients. The accounts that are impersonated need to have read/write access to %SystemRoot%\Microsoft.NET\Framework\<version>\Temporary ASP.NET files directory.

 

7)      Run web app under dedicated application pool

 

Create a dedicated application pool for your Web application and change identity of the application pool to a domain account. For information about how to create an application pool for a Web application and change the identity of the application pool, see Appendix below.

 

8)      Add service account under IIS_WPG group

 

Add the domain account that you used when you set the identity of the application pool to the IIS_WPG group. For information about how to add an account to a group such as IIS_WPG, see appendix.

 

9)      Connect to AT using full URI of AT

 

WIT OM running in WebApp does not have information of how friendly names correspond to URI of AT, hence in code, connect using URI such as http://<server-name>:<port>.

 

 

Recommendations on using WorkItemStore object in web:

 

Creating WorkItemStore objects is costly because 1) it makes a DB call to get metadata cache changes and 2) creates many data structures and creates logical map of metadata in memory. Hence it is a good idea to cache WorkItemStore objects in session scope per user session. WorkItemStore pooling techniques could also be used. Caching it in application or page scope has below issues:

 

Application Scope: OM calls are blocked on single lock in work item store and hence all calls are serialized per store. Hence using single work item store per applications would have big performance implication as all db/om calls will get serialized. Also, each user will have different security profiles and using same store for users of different security profiles is not tested.

 

Page Scope: Creating workitemstore instances per page is costly because of costs specified above. However this may be good solution for certain applications.

 

 

Known Issues to watch out for:

 

(Update on 5/8/07)

Getting exception “Requested registry access is not allowed” when connecting with impersonation:

 

This bug was reported and we are investigating a fix. We do not have a consisten repro yet. Workaround for now is to give all connecting users read access to the registry key in machine for resolving server names. The key name is HKEY_USERS\.Default\Software\Microsoft\VisualStudio\8.0\TeamFoundation\Servers. See this forum post.

 

(Update on 9/15/06)
If You get error 'Cannot pass GCHandle across AppDomain':

 

I thought it is worth mentioning below excerpt from http://www.goeleven.com/blog/entryDetail.aspx?entry=35 (thanks to Buck for pointing it out). I haven't verified it, but this makes sense. For further details such as hotfix status, check http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=586269&SiteID=1. (Update on 6/27/07) We made a fix for this in SP1, but still below apply as it is related to ASP.Net. We also noticed that having a dedicated app pool for webaccess resolves this for some customers. This error occures when there are more than one app domains within a process.

Whenever you create an instance of the WorkItemStore class, a cache is create on the local disk. By default this local folder is located in the current users profile. This works fine whenever you use the API in a winforms application, but not for an ASP.NET application. ASP.NET applications run under the NETWORK SERVICE by default and that profile is inaccessible.

To change this default behaviour, you can set WorkItemTrackingCacheRoot property of the TeamFoundationServer instance in code or in your configuration file's appSettings section.

But, setting the cache on local disk incurs some risk to. Whenever you set the cache to a folder inside the website's virtual directory you will encounter the following error each second call to the workitem store: 'Cannot pass GCHandle across AppDomain'. This issue is caused by an unmanaged component called CProdStudio, which is cannot be reached for cleanup as it is defined in an other application domain. At first, I was quite astonished by this behaviour. I allways thought ASP.NET applications ran in a single AppDomain unless something changed to the configuration file or one of the assemblies. This seems to be only partially correct, in fact ASP.NET monitors all file changes in the application and as soon as this count hits a mere 15, it will automatically recycle and create a new AppDomain. As the workitem cache is a file store which will be target of multiple write operations, it will allmost allways cause a recycle of the AppDomain.

Getting rid of this annoying behaviour can be achieved by setting the numRecompilesBeforeAppRestart attribute of the compilation tag in your application's configuration file to a very large number or better, move the cache to a folder outside the website's virtual directory.

<add key="WorkItemTrackingCacheRoot" value="D:\Data\Cache" />

Appendix 1: Sample Code

Below is sample code to run under Forms authentication mode. When using impersonation mode, the credentials need not be specified and impersonated user’s credentials will be used.

 

using System;

using Microsoft.TeamFoundation.WorkItemTracking.Client;

using Microsoft.TeamFoundation.Client;

using System.Net;

 

public partial class Test : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        // Get the username/password/domain from a OWA like web form and create credential object

        NetworkCredential networkCredential = new NetworkCredential("JoeUser", "JoePassword", "MyDomain");

        ICredentials credential = (ICredentials)networkCredential;

 

        // Get server name possibly from web config and create TeamFoundationServer and Store in user's credential

        TeamFoundationServer server = new TeamFoundationServer("http://MyServer:8080", credential);

        WorkItemStore store = (WorkItemStore) server.GetService(typeof(WorkItemStore));

 

        // Use the store

        Label1.Text = store.TeamFoundationServer.AuthenticatedUserDisplayName;

        Label2.Text = "Number of projects: " + store.Projects.Count.ToString();

    }

}

 

 

Check the attached document for below appendixes on step-by-step instructinos.

 

Appendix 2: How to Enable Integrated Windows Authentication and Turn Off Anonymous Access

Appendix 3: Create an Application Pool for a Web Application