Configuring SSO for MOSS with split-farm back-to-back topology

I had to configure SSO on a MOSS farm with split back-to-back topology with the Web Front Ends (WFEs) in the perimeter network belonging to one domain and SQL Server in the internal network belonging to another domain (there was a trust relationship defined between the two domains). All the service accounts, farm accounts, and database access accounts being used came from the internal domain.

When I tried to configure the SSO service (ssosrv.exe) to use an account from the internal domain, I would get the following error:

A Single Sign-on error has occurred.  Please contact an administrator.  Details: The network path was not found.

There would also be a corresponding entry in the event log like this:

User internaldomain\internaluser failed to configure the single sign-on server. The error returned was 0x80630005. Verify this account has sufficient permissions and try again.

The account used absolutely had sufficient permissions. It met all the criteria specified in http://technet.microsoft.com/en-us/library/cc262932.aspx for the ssosrv.exe service account. I tried and re-tried with other accounts, making the account a domain admin, giving it every SQL Server permission known to man (jk) and it still didn’t work. I even manually created the SSO database myself and ran the SSO schema script myself and manually added permissions to it but I still couldn’t get SSO configured in Central Administration. Actually, I had ruled out issues with SQL Server permissions earlier because when I checked the SQL Server logs, there weren’t even any log entries for login failures. So I sort of came to the conclusion that something else was happening on the WFE before it even got to the database.

After struggling with the configuration for a while and looking at many error log entries, I finally got it configured. Looking at the ULS logs actually helped me resolve the issue because I noticed the following entry:

Net{User|Group}GetInfo said Account {internaluser} does not exist   

What a strange error. Clearly, SSO configuration was attempting to use the command-line command NET USER internaluser to get information about the account being used by ssosrv.exe. But issuing that command goes against the current domain of the machine, which in this case was the domain of the perimeter network and not the internal network domain where this account really came from. By switching the account used by ssosrv.exe to an account that instead came from the perimeter domain and adding that account to SQL Server (and making sure it conformed to the criteria as outlined in the article above), I was able to successfully configure the SSO.

Just another example where the error displayed hardly gets you down the right path.

When to dispose of the SPSite.RootWeb object

A few weeks ago, I was doing some code reviews when I ran into the following snippet of code:

Example A
SPWeb web = new SPSite(siteGuid).RootWeb;

I told the developer who wrote the code to dispose of these objects he created per the suggestions in the Best Practices: Using Disposable Windows SharePoint Services Objects article. The next morning, the developer sent me an email pinpointing a piece of code he thought I wrote that to him seemed to be the same type of code as his where the objects weren't being destroyed. This was the piece of code:

Example B
SPWeb web = SPContext.Current.Site.RootWeb;

So what was the deal here? Was this a case of "do as I say, not as I do"? No, it wasn't. First of all, the code he was citing wasn't actually written by me, it had been written by another developer. I was, however, the reviewer. Second, I was concerned not only with the web object in his code but the SPSite object he "news up". Both of those objects need to be destroyed which he wasn't doing. At the very least, Example A code should have been rewritten as:

SPSite site = new SPSite(siteGuid);
SPWeb web = site.RootWeb;
//do stuff
web.Dispose();
site.Dispose();

One thing you'll notice here is that I keep a reference to the SPSite object that is being created. That is because this object will later need to be disposed of and by not keeping a reference to this object, there is no way we would be able to do that.

Now back to the RootWeb object, which this developer was pointing out to me also wasn't being disposed of in Example B code. So why not? Is this bad practice not to dispose of the RootWeb object? If you read the best practices article, it clearly states that the SPSite.RootWeb property creates a new object and assigns this new instance to a local member variable. Each subsequent access will be referencing the object stored in the local member variable. Basically, the RootWeb object is lazy loaded on first access. The article later warns that after you're done using the RootWeb property, you should dispose of it. Clearly, Example B doesn't do that. However, not everything is as straightforward as this article suggests.

In the example code in the article, the RootWeb object being accessed is from an SPSite object that was created explicitly. In Example B above, the RootWeb object is being accessed from the SPContext.Current.Site object. As most of you know, the Site and Web properties from SPContext.Current are special in that the disposal of these objects are managed by SharePoint and should not be disposed of explicitly. So what happens then if you dispose of RootWeb from SPContext.Current.Site? Will you get the dreaded "Trying to use an SPWeb object that has been closed or disposed and is no longer valid" error message you might get if you disposed of the Site or Web objects? No you wouldn't. That's because the getter method of the RootWeb property checks to see if the local member variable web object is null. If it is null, then the root web object will just be created and it will be assigned to a local member variable (the lazy loading described above). When the RootWeb is disposed, the reference is set back to null. So any subsequent calls to the property after RootWeb has been disposed of will just reconstruct the object again. So you could dispose of it if you want. But for performance reasons, my suggestion is you shouldn't.

The particular line of code in Example B is actually inside a method in a class that was created to handle a custom localization solution. Potentially, this method could be called 20-25 times per page request. If the RootWeb.Dispose() method is called within this method, then that would mean we would be creating and destroying the same object 20-25 times per page request. That will affect performance. But if I'm suggesting that you don't explicitly dispose of this object you should be asking yourself if we are then creating a memory leak. The answer is no.

When an SPWeb object is created, a reference to this object is added to an internal list of SPWeb objects maintained in the SPSite object to which the SPWeb object belongs. When the SPSite object is disposed, all SPWeb objects in this internal list (m_openedWebs) are iterated over and have their Dispose() methods called also. Since a reference to the root web is added to this internal list of the SPContext.Current.Site object, even though we don't call the SPContext.Current.Site.RootWeb.Dispose() method explicitly, the RootWeb object will eventually be disposed when SharePoint has finished the request and disposes SPContext.Current.Site for us. By following this suggestion, RootWeb will be created and destroyed only once per page request rather than the 20-25 times it would have been had we called RootWeb.Dispose() explicitly.

If you're interested in developing on the SharePoint platform, these are some of the considerations you need to be aware of. As I mentioned in my previous blog entry, in order to really write good SharePoint code, it is important to know the SharePoint object model's implementation. Otherwise, it is very easy to write inefficient code even while we believe we're following all the guidelines and the best practices.

 

UPDATE: It looks like the Best Practice document has been updated. It is now NOT RECOMMENDED for you to dispose the RootWeb directly. Looks like they've acknowledged that this is being handled internally, as I mentioned above. This is the text from the MSDN article: 

SPSite.RootWeb Property

An earlier version of this article indicated that the calling application should dispose of the SPSite.RootWeb property just before disposing of the SPSite object that is using it. This is no longer the official guidance. The dispose cleanup is handled automatically by the SharePoint framework. Additionally, SPSite properties LockIssue, Owner, and SecondaryContact used the RootWeb property internally. Given the updated guidance for RootWeb, it is no longer advisable to call the Dispose method on the SPSite.RootWeb property whenever any of these properties are used.