Multiple Applications with built-in MembershipProvider and RoleProvider Part II

23. August 2008

As I mentioned in an earlier post, the SqlRoleProvider is not really designed to work when the SqlMembershipProvider points to a different application. You would want to do such a thing if you were creating a suite of application sites for which you wanted a single user list but wanted each application to control its roles and role membership. The SqlRoleProvider and SqlMembershipProvider, worse that not providing this functionality, make you think it is provided by having an applicationName attribute on each one's configuration section. Alas, regardless of what you enter for the SqlMembershipProvider's applicationName, the RoleProvider's applicationName will always trump.

The solution I devised was to create a custom membership provider that derives from SqlMembershipProvider. I then overwrote the following methods:AddUsersToRoles, FinUsersInRole, GetRolesForuser, GetUsersInRole, IsUserInRole and RemoveUsersFromRoles. In addition, I copied the application stored procedures into renamed ones called aspnet_Custom_UsersInRoles_X where I allowed for both the UserApplicationName and the RoleApplicationName to be passed. 

The big advantage of going this route is that it makes it easier to combine the ActiveDirectoryMembershipProvider with the SqlRoleProvider in that you are inheritantly allowing for the two to come from different sources.

.NET Development

Passing RoleProvider roles to Classic ASP

23. August 2008

I struggled for a couple of days trying to work this one out. In short, we have a system which places a .NET veneer on top of ASP Classic to provide security. The trick in this scheme is to add a Wildcard mapping that points the the 2.0 aspnet_isapi.dll so that all requests are routed through .NET. Now, if you are using XP/2003 only, then one solution for then passing the roles to ASP Classic is to add a custom header in a .NET HttpModule. Unfotunately, in IIS7 (and thus Vista), it does not appear that you can add custom headers once you have the User principal instantiated. Ideally, you'd be able to do this in the PostAuthorize event but alas, it is not to be.

I tried numerous approaches and nothing seemed to work. In the end the solution was to create an HttpModule that wrote the username in plain text in a new cookie in the Response and to read that cookie from ASP Classic and use it to query for the Roles. Theoretically, the (more) ideal solution would be for ASP Classic to decrypt the FormsAuthentication cookie. However, to do that, you must read the site's Web.config file or the Machine.config file for the decryption key (and encryption algorithm). I had toyed with the idea of writing a .NET COM component that would do this, but it would still be a bear and I ran into issues getting the COM component to instantiate in ASP Classic (i.e. Server.CreateObject would sporatically throw an error). 

.NET Development

SQL Server 2005 Linked Server Error - "data that does not match expected data length"

23. August 2008
Ran into this error today while trying to do an import. The scenario is that I had a linked server to an MS Access database. After a bit of search and using the force, I discovered that the problem was due to fields set to an empty string as opposed to Null. Running a quick update query that set the given column value to Null where its length was less than one did the trick.

.NET Development

Multiple Applications with built-in MembershipProvider and RoleProvider

19. August 2008

The built-in RoleProvider has, IMO, a major flaw in its design.  Both the RoleProvider and the MembershipProvide allow for an "applicationName" attribute to be set. However, you quickly find out that when you have a RoleProvider configured, that the Membership static methods simply ignore the applicationName on the Membership provider and instead uses the applicationName from the RoleProvider. The assumption being that the source of the Roles is the same as the source for the Users. That's fine and dany when this assumption is true. However, what happens when you are say building a suite of applications that should each have their own roles, but you want to use a centralized list of users? An example, might be a suite of applications installed at a customer where the customer desires to use the ActiveDirectoryMembershipProvider.

As it stands now, the only way to accomplish this feat is to use AzMan for all of your applications. Unfortunately, AzMan is a bear to use and does not provide the ability to create a web interface whereby you can call AddUserInRoles. What the user really wants in this situation is the user search functionality so that they can search for users on their Active Directory domain. Of course you can do this via the AzMan mmc, but if you ever have to hand off your application for someone else to install, you quickly realize that you have to write a bazillion instructions for configuring AzMan which is a problematic pain in the neck (see Commerce Server 2007).

What would be peachy would be a way of using the SqlRoleProvider with ActiveDirectory. That is what I have worked together and I'll post the code in an ensuing post. However, in general the idea is to have a SqlRoleProvider that you use for all authorizations but allow for a different MembershipProvider for authentication and then store a flag which determines whether authenticated users should automatically be added to a given role in the SqlRoleProvider store.  In effect, you are always using the SqlRoleProvider but ensuring that authentication is always done via the cutomer's preferred MembershipProvider.

.NET Development

Forcefully kill a process

19. August 2008
One of the many useful tools that comes with the XP Support Tools is a utility called TaskKill. TaskKill lets you dump a process that refuses to let you kill it from the Task Manager. On occasion, I have run into processes that would throw an "Access is denied" error when I try to kill it from the Task Manager. Once you have installed the XP Support Tools and have it as part of your path, simply type taskkill /? to get more information.

.NET Development, Unintended Consequences

"Selected filter" error when installing VS 2005 MSDN help

18. April 2008

So, my machine committed suicide the other day requiring a complete rebuild. In doing that, I re-discovered a dirty lesson about reinstalling all of your software: you have to do it in a specific order. In my case, I had an instance of Access 2000 (bleh), Access 2003 (newer bleh), Office 2007, VS 2005 SP1, SQL 2005 Developer with SP2, Red Gate SQL Compare, and Data Compare, DBGhost, SourceGear Vault and host of other stuff. On installing in the order that I did I got this gem from help within VS:

The selected filter contains an error that prevents it from being applied.

None of help categories were available. Lovely. After a day and half of uninstall/reinstall hell, I finally downloaded the MSDN library from Microsoft and used that. Miracle of miracles, it worked.

It would be peachy if Microsoft issued a tool you could use to ensure that none of the files were corrupt. I had used this very install previous with no problems but now it was suddenly corrupted.

.NET Development, General

Error using ADAM and CreateUserWizard

11. April 2008

Ran into this gem today. I have an app using the ActiveDirectoryMembershipProvider connecting to an ADAM instance.  While debugging over a VPN connection from my development machine, I ran into this error when the CreateUserWizard tried to create an account.

DirectoryServicesCOMException (0x80072020): An operations error occurred. (Exception from HRESULT: 0x80072020)]

This was followed by a monsterous stack trace ending with System.DirectoryServices.DirectoryEntry.Invoke.

Isn't that descriptive? ;-> Anyway, through much digging I discovered that the problem is that ADAM does not like to accept plaintext passwords over an unsecure connection (meaning without SSL or some sort of ADAM-level security). A quick solution is to tell ADAM to allow such passwords by doing the following:

  1. Open the ADAM ADSI Edit tool
  2. Right-click on the top most node called "ADAM ADSI Edit" and hit "Connect To..."
  3. In the Connection Settings dialog choose "Configuraiton" for Well-known naming context.
  4. Navigate to CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration (folder-wise, remember you read backwards)
  5. Right-click on the "Directory Service" node and hit Properties
  6. Change the property "dsHeuristics" to 0000000001001

 

You can read more at:

Anonymous LDAP operations to Active Directory are disabled on Windows Server 2003 domain controllers  

DS-Heuristics Attribute 

.NET Development

First Look at IE8

9. April 2008
I'll give IE8 this, it is much faster than either IE7 or Firefox. I would say that it is safely 50% faster on average. In addition, wow does it break most sites. In fact, as far as I can tell, none of the Microsoft ASP.NET controls nor the Telerik controls are CSS 2.1 compliant. 

.NET Development

Restarting the application pool using Nant

5. April 2008

I've had times where CruiseControl would not build because files were in use. This is despite the fact that I stop IIS prior to deploying files. So, I modified my deployment build script to also recycle the application pool. This solution builds on information from Artur Carvalho's blog.

 

<property name="appPoolName" value="DefaultAppPool" readonly="false" overwrite="true" />
<script language="C#">
    <references>
      <include name="System.DirectoryServices.dll" />
    </references>        
    <imports>
        <import namespace="System.DirectoryServices" />
    </imports>
    <code>
        <![CDATA[
        public static void ScriptMain( Project project )
        {
            string appPoolName = project.Properties["appPoolName"];
            DirectoryEntry apppools = new DirectoryEntry("IIS://localhost/W3SVC/AppPools");
            DirectoryEntry newpool = apppools.Children.Find(appPoolName,"IIsApplicationPool");
            newpool.Invoke("stop",new object[]{});
            newpool.Invoke("start",new object[]{});
            newpool.CommitChanges();
        }
        ]]>
    </code>
</script>

 

.NET Development