The true power of the ASP.NET SQL membership infrastructure is fully felt as a triumvirate: authentication, authorization, and profiles. Until now, the Hybrid Provider has only manifested two-thirds of this possible functionality; indeed, a "biumvirate." Allowing SharePoint authentication and profiling simultaneously against AD and SQL has been the extent of what it can do.
But now I'm very happy to finally bring the Hybrid Provider full circle in its third release, incorporating the final piece of the membership puzzle: authorization - implemented by the Hybrid Roller.
The Hybrid Roller does its thing for authorization in the same spirit as the Hybrid Provider and Roller do their thing for authentication and profiling, respectively: allow both AD and SQL groups to be used for SharePoint authorization. The reason the Roller has been so long in its coming is because I always ended up being able to get away with using SharePoint groups to do authorization in my portals.
Rarely did existing AD groups map exactly to what I needed in SharePoint; creating new groups (regardless of "what" these groups were) was easier then leveraging and aggregating AD groups. And since a permission best practice in SharePoint is to have only groups in permission roles (not loose users), we would always create one SharePoint for each role definition, and add whomever we need to that group.
However, as more and more SharePoint intranets and extranets started to use the Hybrid Provider to manage their users, I decided to move forward and implement a role provider so that enterprises who have an intense AD forest already set up don't need to rely on reinventing the wheel with SharePoint groups.
Also, introducing SQL groups into the mix gives us the ability to define a more explicit group schema. In other words, your organization can maintain group membership according to any requirements, naming conventions, or jargons in place by customizing the SQL membership database. And by storing authorization information in a database, we can easily provision and scale new and existing SharePoint (or any ASP.NET) applications and bring users, groups, and profiles along for the ride seamlessly.
From a technical perspective, the Hybrid Roller works the same as the Hybrid Provider. It has member providers for SQL and AD that it wraps and calls when appropriate. By "appropriate" I mean a few things. First of all, it always "tries" SQL first, since AD calls (especially groups) are expensive and COM-y; we don't want our external users (who are indeed usually clients, partners, or consultants) to wait while we burn calls to AD!
Secondly, and again like the Hybrid Provider, the Roller considers AD to be read only. Therefore, calls to methods such as "DeleteRole" and "AddUsersToRoles" simply do not invoke the Active Directory role provider. Essentially, we want to make sure to only make AD calls when absolutely necessary.
Let's dig into these member providers a bit. As with it's two big brothers, the Roller uses the out-of-the-box SqlRoleProvider class to implement the SQL side of things. This provider really only needs a connection string and an application name, which are already provided by the Hybrid Provider and are wired up automatically during installation.
For AD, I hit a wall. Indeed, there is a WindowsTokenRoleProvider class that allegedly does for groups with the ActiveDirectoryMembershipProvider does for users. However, I remain unconvinced. I mean, from what I've read it appears to be what User.IsInRole calls behind the scenes; I know this method works. However, for the life of me, I could not get it to work in SharePoint!
Now I hate giving up, but compared to the authentication and profiling, authorization has the simplest configuration, so there's not that many settings to get wrong. I was also turned off because a description of this provider on MSDN seemed to imply that it only did local Windows groups, not AD.
This didn't seem to be very worthwhile to me, and it was a possible case of programmers just not being very clear & concise writers (I'm guilty too, I know). But I gave up anyways and instead wrote my own AD role provider, named "Microsoftively" the ActiveDirectoryGroupProvider.
This puppy actually implements many more methods of the base RoleProvider class than the WindowsTokenRoleProvider. SharePoint only uses a few of them, but since I had a good base provider going, it wasn't much a stretch to implement a few more methods.
And speaking of implementation, there are two important things to point out. First of all, my AD group code is recursive. To have true custom authorization working, especially in a place like a SharePoint extranet, we need to make sure that all of a user's permissions shine through. So if an AD group is given rights to a site in SharePoint, and you are in a group that's in a group that's in a group that's in this group, you had better be able to browse there!
Secondly, is caching. Since these AD calls (like I said before, especially group calls - and like I'm saying now, ESPECAILLY recursive groups calls) are expensive, we want to cache the results to make sure subsequent pages load much faster. The role provider infrastructure actually has some out-of-the-box support for caching results in cookies, which, again, I would have happily leveraged if it appeared to me as though it were a no brain-er.
But I didn't observe any differences in call times when I tried this method. And, umm, cookies? Really? Are we still using cookies when we have technologies like ASP.NET and Silverlight out there? No cookies. Cookies are fattening. So, again, i wrote it myself. :) My caching code is pretty basic. I created an object that has a few dictionaries of users in roles, and roles for users, some helper methods, and store it in the global Cache Cache object.
And as with any caching, we need to make sure that it expires so that "stale" data isn't stored. By default, the cache expires absolutely after 12 hours, or whenever an IISRESET is executed. The number of hours to stored cached Hybrid Roller calls is a configuration setting, so that you can tune this to your organization's frequency of changing group information. Finally, ONLY AD calls are cached. Since SQL calls are only a stored procedure execution and enough .NET code to invoke it, they will always be fast and simply not work caching.
That's it! Like I said, with the Hybrid Roller, I have finally implemented all of the major facets of "Hybrid" membership, and brought them to SharePoint. You can download the source code, or just the installer, here. Again, to upgrade, run this installer, uninstall the Hybrid Provider from your web application(s) and then reinstall.
In addition to the new Hybrid Roller, I've made some updates around the rest of the Hybrid family as well. The picture control for the Hybrid Profiler now uses an ASPX page and GDI to resize pictures so that they look better on the profile page. Also, you have the option to not select an image for someone.
I also updated all the features' (Provider, Profiler, Roller, and AJAX) XML files to contain the following setting: ActivateOnDefault="FALSE" in their markup. I noticed that after using my installer to setup the Hybrid Provider, subsequent new web applications would have it installed already. And then, when debugging some application provisioning code, my feature receivers for the Hybrid Provider would be invoked! Wow!
Well, I decided that this was not intuitive behavior, so I turned it off. If you already have the Hybrid Provider installed (and don't want to mess with it), here's how to make this change manually so you don't have the Hybrid Provider hanging out on new web applications that you don't intent it to:
On a completely unrelated note, I made an addition to my Web Part Page Manager. I added a "Page Clearer" web part that removes all web parts and MOSS Publishing field data from a page. Also, I changed the web parts to be actual web parts, not user controls hosted as Smart Parts. When I was testing the Page Clearer, I noticed that it took just as long to add a SmartPart, wire up my user control, and publish the page than it did to just manually delete all the web parts.
Other quick changes include renaming the "Zone Hider" to the "Zone Expander." Now you just add the web part to a zone, and the width of that zone is set to 100%; all other zones are hidden. Finally, when the Zone Expander is deleted, it re-shows all of the zones and resets the page.
And now there's a solution to install it. This is actually my first crack at solution deployment; I usually wuss out and use features and batch files. So to install the Web Part Page Manager, add and deploy the solution to your farm, making sure the intended destination site collection is specified in your STSADM call. There's an "Install.bat" file in the project source code folder that you can run that will retract and deploy the solution for you.
That's all! Have fun!