Hiding Default Authentication Providers in SharePoint 2010

A scenario that is happening more frequently in SharePoint 2010 is using a single zone for multiple authentication providers.  One of the reasons folks do this is because they want to use a some type of claims authentication – like FBA or SAML – but they also want to add Windows claims so that the zone can be indexed by the SharePoint crawler.  The problem with that approach is two-fold:

  1. Users see a login selection page where they need to select either Windows or some other type of authentication
  2. When users are added to a site (or really any operation is performed that invokes the people picker), they will see Active Directory users in the picker results along with users for the claims authentication providers they are using.

So, what can we do about that?  Well to solve the first problem, we can write a custom login selection page.  I’ve covered that scenario already in my posting at http://blogs.technet.com/b/speschka/archive/2011/04/30/bypassing-the-multi-authentication-provider-selection-page-in-sharepoint-2010.aspx.  But what about the second item – hiding AD users from showing up in people picker search results?  In comes the April 2011 CU to the rescue! <QUICK NOTE: You may experience some issues with this in the April CU; the June CU will have an updated version.  Your mileage may vary so please feel free to test.>  Once you’ve applied the CU you will see that your SPClaimProviderDefinitions now include a new property called “IsVisible”.  You can simply set this to false for the Active Directory provider and it will no longer show up when you use the people picker.

Here’s a little PowerShell snippet that shows you how to do this:

$cpm = Get-SPClaimProviderManager
$ad = get-spclaimprovider -identity “AD”
$ad.IsVisible = $false
$cpm.Update()

A couple of things to note:

  1. The PowerShell command Get-SPClaimProvider actually returns an SPClaimProviderDefinition, so you’re good there.
  2. The identity “AD” is used because that’s the internal name of the Active Directory provider.

In my limited testing I didn’t even have to do an IISRESET after making this change; I could just go in and Active Directory no longer showed up in the list of authentication providers in the left pane of the people picker.  Conversely when I changed it back it started showing up again immediately without an IISRESET.

At this time the biggest limitation with this is that it does not appear you can enforce this setting on a per-zone basis, which would be ideal.  If I find out otherwise about that I will update this post.

 

 

 

Advertisements

Tips for ULS Logging Part 2

In part 1 of ULS logging tips (http://blogs.technet.com/b/speschka/archive/2011/01/23/tips-for-uls-logging.aspx) I included a some code to add a custom ULS logging Area and demonstrated how to use it within your project.  After working with it a bit I noticed a few things:

1.       There was some code in there that didn’t jive up with the latest SDK – basically some code pieces implemented that the SDK implied I should not need, and some stuff that I was doing slightly differently than the SDK example (which, by the way, needs to be updated and I will work on that separately).

2.       Logging would only occur when I set the TraceSeverity to Medium; there was not an effective way to layer in different trace levels.

3.       The most super annoying part – when I tried to invoke my custom class that is required for my custom Area, it would fail when I tried doing any logging during a POST activity.  It triggered the all too common error about not being able to update an SPWeb during an POST unless setting the AllowUnsafeUpdates property to true.  Now setting that on an SPWeb during a log event, just to make my log event work, seemed borderline insane (my apologies to all the insane people out there by the way).  So I decided there must be a better way.

In this posting then, I’m going to improve upon the previous example and add to it along the way.  Here’s what we’re going to show:

1.       An improved class for the custom Area – it actually slims down some in this version.

2.       Integration with Configure diagnostic logging in the Monitoring section of Central Admin.  This integration will further allow support for configuring tracing levels on the custom Area and Category.

3.       Registration information – a very simple way to both register and unregister the custom ULS logging class so it integrates with Central Admin, and can be removed from Central Admin.

To begin with, let’s look at the new streamlined version of the class.  There are many similarities between this and the version I showed in the first posting, but this is a little slimmer and simpler.  Here’s what it looks like now.
    [Guid(“833B687D-0DD1-4F17-BF6A-B64FBC1AC6A8”)]
    public class SteveDiagnosticService : SPDiagnosticsServiceBase
    {
 
        private const string LOG_AREA = “Steve Area”;
 
 
        public enum LogCategories
        {
            SteveCategory
        }  
 
 
        public SteveDiagnosticService()
        {
        } 
 
 
        public SteveDiagnosticService(string name, SPFarm parent)
            : base(name, parent)
        {
        } 
 
 
        public static SteveDiagnosticService Local
        {
            get
            {
                return SPDiagnosticsServiceBase.GetLocal<SteveDiagnosticService>();
            }
        }  
 
 
 
        public void LogMessage(ushort id, LogCategories LogCategory,
TraceSeverity traceSeverity, string message,
params object[] data)
        {
 
            if (traceSeverity != TraceSeverity.None)
            {
                SPDiagnosticsCategory category
                 = Local.Areas[LOG_AREA].Categories[LogCategory.ToString()];
                Local.WriteTrace(id, category, traceSeverity, message, data);
            }
 
        }
 
 
        protected override IEnumerable<SPDiagnosticsArea> ProvideAreas()
        {
yield return new SPDiagnosticsArea(LOG_AREA, 0, 0, false,
new List<SPDiagnosticsCategory>()
{
new
SPDiagnosticsCategory(LogCategories.AzureConfig.ToString(),
                     TraceSeverity.Medium, EventSeverity.Information, 1,
                     Log.LOG_ID)
                     });
        }
    }
 
Here are the important changes from the first version:

1.        Add the Guid attribute to the class:

[Guid(“833B687D-0DD1-4F17-BF6A-B64FBC1AC6A8”)]
 
I added a Guid attribute to the class because SharePoint requires it in order to uniquely identify it in the configuration database.

2.       Changed the default constructor:

        public SteveDiagnosticService()
        {
        } 
 
Now it’s just a standard empty constructor.  Before I called the other overload for the constructor that takes a name for the service and an SPFarm.  Just less code is all, which is a good thing when you can get away with it.

3.       Deleted the HasAdditionalUpdateAccess override.  Again, turned out I wasn’t really using it, so continuing with the “less is more” theme I removed it.

 

4.       Shortened the ProvideAreas method up significantly; now it matches the same pattern that is used in the SDK:

 

yield return new SPDiagnosticsArea(LOG_AREA, 0, 0, false,
new List<SPDiagnosticsCategory>()
{
new
SPDiagnosticsCategory(LogCategories.AzureConfig.ToString(),
                     TraceSeverity.Medium, EventSeverity.Information, 1,
                     Log.LOG_ID)
                     });
So that addressed problem #1 above – my code is now a little cleaner and meaner.  The other problems – lack of tracing levels, throwing an exception when logging during a POST, and integration with central admin – were all essentially fixed by taking the code a bit further and registering.  The example in the SDK is currently kind of weak in this area but I was able to get it to do what we need.  To simplify matters, I created a new feature for my assembly that contains my custom ULS class I described above.  I added a feature receiver and it, I register the custom ULS assembly during the FeatureInstalled event, and I unregister it during the FeatureUninstalling event.  This was a good solution in my case, because I made my feature a Farm scoped feature so it auto activates when the solution is added and deployed.  As it turns out, the code to do this register and unregister is almost criminally simple; here it is:
public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
try
       {
SteveDiagnosticsService.Local.Update();
}
catch (Exception ex)
       {
throw new Exception(“Error installing feature: “ + ex.Message);
}
}
 
 
public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
try
       {
SteveDiagnosticsService.Local.Delete();
}
catch (Exception ex)
{
throw new Exception(“Error uninstalling feature: “ + ex.Message);
}
}
 
The other thing to point out here is that I was able to refer to “SteveDiagnosticService” because I a) added a reference to the assembly with my custom logging class in my project where I packaged up the feature and b) I added a using statement to my feature receiver class for the assembly with my custom logging class.
By registering my custom ULS logging class I get a bunch of benefits:

·         I no longer get any errors about updating SPWeb problems when I write to the ULS log during a POST

·         My custom logging Area and Category shows up in central admin so I can go into Configure diagnostic logging and change the trace and event level.  For example, when it’s at the default level of Medium tracing, all of my ULS writes to the log that are of a TracingSeverity.Medium are written to the log, but those that are TracingSeverity.Verbose are not.  If I want to have my TracingSeverity.Verbose entries start showing up in the log, I simply go Configure diagnostics logging and change the Trace level in there to Verbose.

Overall the solution is simpler and more functional.  I think this is one of those win-win things I keep hearing about.
P.S.  I want to record my protest vote here for LaMarcus Aldridge of the Portland Trailblazers.  His failure to get added to the Western Conference NBA All Star team is tragically stupid.  Okay, enough personal commentary, I know y’all are looking for truly Share-n-Dipity-icous info when you come here.  Hope this is useful.

Tips for ULS Logging

UPDATE 2-4-2011:  I recommend taking a look at the updated example for this at http://blogs.technet.com/b/speschka/archive/2011/02/04/tips-for-uls-logging-part-2.aspx.  The new example is better and more functional.

When I added some ULS logging to a recent project I noticed one annoying little side effect.  In the ULS logs the Area was showing up as “Unknown”.  I realize that some aspects of this have been covered other places but I just wanted to pull together a quick post that describes what I found to be the most expedient way to deal with it (certainly much less painful than some of the other solutions I’ve seen described).  One thing worth pointing out is that if you don’t want to do this at all, I believe the Logging Framework that the best practices team puts up on CodePlex may do this itself.  But I usually write my own code as much as possible so I’ll briefly describe the solution here.

The first thing to note is that most of the SDK documentation operates on the notion of creating a new SPDiagnosticsCategory.  The constructor for a new instance of the class allows you to provide the Category name, and when you do that you will definitely see any custom Category name you want to use show up in the ULS log in the Category column.  In most cases if you’re doing your own custom logging you also want to have a custom Area to go hand in hand with your custom Category(s).  Unfortunately, the SDK puts you through a considerably more complicated exercise to make that happen, because you cannot use a simple constructor to create a new Area and use it – you have to write your own class that derives from SPDiagnosticsServiceBase.

The way I’ve chosen to implement the whole thing is to create one CS file that contains both my logging class and my diagnostic service base class.  I’ll start first with the diagnostic base class – below I’ve pasted in the entire class, and then I’ll walk through the things that are worth noting:

    public class SteveDiagnosticService : SPDiagnosticsServiceBase

    {

 

        private const string LOG_AREA = “Steve Area”;

 

 

        public enum LogCategories

        {

            SteveCategory

        }  

 

 

 

        public SteveDiagnosticService()

            : base(“Steve Diagnostics Service”, SPFarm.Local)

        {

        } 

 

 

 

        public SteveDiagnosticService(string name, SPFarm parent)

            : base(name, parent)

        {

        } 

 

 

 

        protected override bool HasAdditionalUpdateAccess()

        {

            return true;

        }

 

 

        public static SteveDiagnosticService Local

        {

            get

            {

                return SPDiagnosticsServiceBase.GetLocal<SteveDiagnosticService>();

            }

        }  

 

 

 

        public void LogMessage(ushort id, LogCategories LogCategory, TraceSeverity traceSeverity,

            string message, params object[] data)

        {

 

            if (traceSeverity != TraceSeverity.None)

            {

                SPDiagnosticsCategory category

                 = Local.Areas[LOG_AREA].Categories[LogCategory.ToString()];

                Local.WriteTrace(id, category, traceSeverity, message, data);

            }

 

        }

 

 

        protected override IEnumerable<SPDiagnosticsArea> ProvideAreas()

        {

 

            List<SPDiagnosticsCategory> categories = new List<SPDiagnosticsCategory>();

 

            categories.Add(new SPDiagnosticsCategory(

             LogCategories.SteveCategory.ToString(),

             TraceSeverity.Medium, EventSeverity.Information));

 

            SPDiagnosticsArea area = new SPDiagnosticsArea(

             LOG_AREA, 0, 0, false, categories);

 

            List<SPDiagnosticsArea> areas = new List<SPDiagnosticsArea>();

 

            areas.Add(area);

 

            return areas;

        }

    }

 

Let’s look at the interesting parts now:

private const string LOG_AREA = “Steve Area”;

Here’s where I define what the name of the Area is that I’m going to write to the ULS log.

public enum LogCategories

{

SteveCategory

}    

This is the list of Categories that I’m going to add to my custom Area.  In this case I only have one category I’m going to use with this Area, but if you wanted to several of them you would just expand the contents of this enum.

public void LogMessage(ushort id, LogCategories LogCategory, TraceSeverity traceSeverity,

string message, params object[] data)

{

if (traceSeverity != TraceSeverity.None)

{

SPDiagnosticsCategory category

= Local.Areas[LOG_AREA].Categories[LogCategory.ToString()];

 

Local.WriteTrace(id, category, traceSeverity, message, data);

}

}

 

This is one of the two important methods we implement – it’s where we actually write to the ULS log.  The first line is where I get the SPDiagnosticCategory, and I make reference to the Area it belongs to when I do that.  In the second line I’m just calling the base class method on the local SPDiagnosticsServiceBase class to write to the ULS log, and as part of that I pass in my Category which is associated with my Area.

protected override IEnumerable<SPDiagnosticsArea> ProvideAreas()

{

 

List<SPDiagnosticsCategory> theCategories = new List<SPDiagnosticsCategory>();

 

theCategories.Add(new SPDiagnosticsCategory(

             LogCategories.SteveCategory.ToString(),

             TraceSeverity.Medium, EventSeverity.Information));

 

SPDiagnosticsArea theArea = new SPDiagnosticsArea(

             LOG_AREA, 0, 0, false, theCategories);

 

List<SPDiagnosticsArea> theArea = new List<SPDiagnosticsArea>();

 

theArea.Add(area);

 

return theArea;

}

 

In this override I return to SharePoint all of my custom Areas.  In this case I’m only going to use my one Area, so that’s all I send back.  Also, as I mentioned above, I’m only using one custom Category with my Area.  If I wanted to use multiple custom Categories then I would 1) add them to the enum I described earlier and 2) add each one to my theCategories List instance I’ve defined in this method.

That’s really the main magic behind adding a custom Area and getting it to show up in the appropriate column in the ULS logs.  The logging class implementation is pretty straightforward too, I’ll paste in the main part of it here and then explain a little further:

    public class Log

    {

 

        private const int LOG_ID = 11100;

 

 

        public static void WriteLog(string Message, TraceSeverity TraceLogSeverity)

        {

            try

            {

                //in this simple example, I’m always using the same category

                //you could of course pass that in as a method parameter too

                //and use it when you call SteveDiagnosticService

                SteveDiagnosticService.Local.LogMessage(LOG_ID,

                    SteveDiagnosticService.LogCategories.SteveCategory,

                    TraceLogSeverity, Message, null);

            }

            catch (Exception writeEx)

            {

                //ignore

                Debug.WriteLine(writeEx.Message);

            }

        }

    }

 

This code is then pretty easy to implement from my methods that reference it.  Since WriteLog is a static method my code is just Log.WriteLog(“This is my error message”, TraceSeverity.Medium); for example.  In this example then, in the ULS log it creates an entry in the Area “Steve Area” and the Category “SteveCategory” with the message “This is my error message”.

List Throttling and Recycle Bin in SharePoint 2010

Hey all, I was just told today about an interesting wrinkle on a new feature in SharePoint 2010.  As I’ve blogged about here before, large list throttling is a new feature that’s designed to limit the impact of large lists on the health of a SharePoint farm.  By default it restricts you from “impacting” more than 5000 rows of data in a single query (like a view, adding an index, etc.).  The new twist on this is the scope of data that is counted in this 5000 number.  It turns out that if you have deleted items from a list, all of those items still can be counted against your 5000 item limit as long as they’re in the recycle bin.  So how, you ask, could items in the recycle bin be used in a view on a SharePoint list?  Well, they won’t show up in a view, of course, but as explained here previously, you can have list throttling kick in on more than just a view of your data.  For example, if you add an index, create a sorted or filtered view with a non-indexed field, etc. – that’s when list throttling can be impacted by items in the recycle bin.

My good friend Tom is the one that noticed this and was able to track down the culprit, so thanks Tom for pointing this out.  I’m sure this info will save some folks valuable time and hair pulling.

Managing Trusted Root Authorities for Claims Authentication in SharePoint 2010 Central Admin

I just thought I would create this post to raise awareness about another way to manage trusted root authorities in SharePoint 2010.  For those of you who have been doing claims authentication sites, you know that you need to add all of the certificates in the token signing certificate’s chain into SharePoint’s list of trusted sites.  All of the previous examples I’ve demonstrated in my blogs has done that through PowerShell.  To give equal coverage, I thought I would just let you all know that you can do the same thing in central administration – add, change and/or delete a trusted root authority for you token signing certificate chain.

To do this in central admin you need to go to Central Admin…Security…Manage Trust.  In that screen you will see all of the trusted root authorities that have been defined.  You can add or edit trusts by browsing for a certificate that SharePoint will upload use to establish a trust.  Just make sure you do not delete the “local” trusted certificate.  That is for SharePoint’s own internal STS.

How Does SharePoint 2010 Support Partitioned Tables in the Usage Database

I was talking with a buddy of mine today about the usage database in SharePoint 2010.  It is by all appearances a set of partitioned tables in SQL Server.  His very good question was well, partitioning tables is only available in the enterprise edition of SQL Server – what happens if I have a different flavor of SQL that I want to put my usage database on?  So I did a little snooping around and it turns out that while the usage database has the appearance of partitioned tables in SQL Server, it really isn’t.  Meaning it doesn’t use the partitioning features provided by SQL Server exactly for reasons like this.  Instead, SharePoint manages all of the tables and data loading so that it carries some of the same features as SQL partitioning but without requiring the overhead of the SQL Enterprise SKU.  Hopefully that satisifies the curiousity of those of you who were wondering the same thing.

Configuring a Custom Claims Provider to be Used only on Select Zones in SharePoint 2010

UPDATE:  I updated the application attachment for this posting.  Before it would only let you toggle a claims provider for a zone if that zone was using SAML claims.  In retrospect that limitation didn’t make a lot of sense, so now it lets you toggle a claims provider for any zone that is using any type of claims – Windows, FBA or SAML.

As the consciousness of using claims providers begins to spread, more questions continue to come up around usage scenarios.  One of the scenarios I’ve heard mentioned a few times is how can I configure my custom claims provider to only be used for a certain web application or two, instead of all of them?  Originally I was hoping that we could solve this rather simply, and just scope the feature for the claim provider to web application instead of farm.  Well, guess what – that won’t work.  So here’s lesson #1 – a feature for a custom claims provider must always be scoped to the Farm level.  If you configure the scope of your feature to be anything different, you will get a “The method or operation is not implemented” error.

So the if the feature is scoped to the farm level, how do we configure the provider only to be used on select web applications and/or zones?  Turns out the answer to that is much more complicated.

To begin with, it’s important to understand two very important properties on a claims provider:  IsEnabled and IsUsedByDefault.  Out of the box, when you install a new claims provider feature, the claim provider has IsEnabled and IsUsedByDefault set to true.  That means that your claim provider will be used everywhere SAML claims are enabled on a zone.  So in order to even be able to configure a claim provider to be used only on select zones, we first need to change the IsUsedByDefault property to false.  As long as IsEnabled equals true, and IsUsedByDefault equals false, we can configure use of the claims provider on a per zone basis.

So, obviously the next question is how can I set the IsUsedByDefault property to false?  Well there are a couple of options here.  One option is to use your feature receiver that you create for your custom claims provider and configure it in there.  In the override for the FeatureActivated event (which you should already have coded anyways), you can use the SPClaimProviderManager to get a reference to your provider and set the IsUsedByDefault property to false.  Here’s an example that I used in one of my providers:

SPClaimProviderManager cpm = SPClaimProviderManager.Local;

 

foreach (SPClaimProviderDefinition cp in cpm.ClaimProviders)

{

if (cp.ClaimProviderType == typeof(SqlClaimsProvider.SqlClaims))

       {

       cp.IsUsedByDefault = false;

              cpm.Update();

              break;

       }

}

 

In this code where it uses typeof(SqlClaimsProvider.SqlClaims), you would substitute the assembly and class name of your provider, like typeof(MyProvider.MyClaims).  You can also wrap the code above in a custom application. This is in fact what I’ve done, and what I’ll also be using to demonstrate how the rest of this works throughout this posting.  Here’s a screenshot of my Claims Provider Activation application, and you can see in the left side of application is a list of my registered custom claims providers (there is only one), and checkboxes below it where I can change the IsEnabled and IsUsedByDefault properties on it:

So as you can see, you just select your custom claims provider, check the boxes as desired for IsEnabled and IsUsedByDefault, then click the Update button.

Now, once your claims provider is configured to be IsEnabled but not IsUsedByDefault, you can configure where it will be used.  Once your claims provider is configured this way, it will only be used when the SPIisSettings for a zone contain the name of the claims provider in the ClaimsProviders property.  Getting to this information is a little more difficult if you’re trying to do it generically, as I did with the Claims Provider Activation application.  The first step was to enumerate all of the web applications, which is done easily enough with the SPService class:

//get the content service

SPWebService ws = SPWebService.ContentService;

 

//get each web app

foreach (SPWebApplication wa in ws.WebApplications)

{

       //enumerate each zone in each web application

}

Enumerating each zone in a web application is a little less common operation – here’s the code for doing that:

foreach (SPAlternateUrl aam in wa.AlternateUrls)

{

                //look at each zone to see if it’s using claims

}

 

To determine whether a zone is using claims, I start by getting the SPIisSettings for the zone; as I mentioned above, I’m going to need this anyway in order to add or remove my claim provider from the ClaimsProviders property.   Here’s how I get those settings:

 

SPIisSettings curConfig = wa.GetIisSettingsWithFallback(aam.UrlZone);

 

In my curConfig variable now I can look at the ClaimsAuthenticationProviders property.  If a zone is configured to use claims, then the ClaimsAuthenticationProviders property will not be null, and the ClaimsAuthenticationProviders.Count() property will be greater than zero.  So I enumerate all of the providers and if I find that one, I add the zone to my list of zones and show it as enabled.  Otherwise I can’t use my custom claims provider with that zone so I add it to the list of zones but show it disabled.  So that’s the basics of how I generate the list of web applications and zones, and determine which zones can use a custom claims provider.

 

With that list then, for any particular zone my claims provider will be used if the claim provider name is in the ClaimsProviders property of the SPIisSettings.  This particular property is actually an IEnumerable of type string, so it’s really a little awkward to work with because it doesn’t contain an overloaded method to Add or Remove or RemoveAt items.  In order to get past that, I took the property and converted it to a List<string> so I could add or remove my claim provider name, and then I assign the List<string> back to the ClaimsProviders property.  Here’s what that code looks like:

 

List<string> providers = theZoneIisSettings.ClaimsProviders.ToList();

 

//NOTE: myClaimProvider is type SPClaimProviderDefinition

if (EnableProvider)

providers.Add(myClaimProvider.ClaimProvider.Name);

else

       providers.Remove(myClaimProvider.ClaimProvider.Name);

 

//plug the new values back into the ClaimsProviders IEnumerable

theZoneIisSettings.ClaimsProviders = providers;

 

//you must get the web application that contains the zone and call its

//update method in order to save your changes

theWebApp.Update();

 

Here’s what this looks like in the Claims Provider Activation Application:

 

 

In this case the menu says “Enable Basketball Teams” because my Basketball Teams provider is not enabled for that zone.  If it were, then the menu would say “Disable Basketball Teams”.  You’ll also notice that the zones above it, in the SharePoint – Anon Profile Test web application are disabled.  That’s because those zones are not configured to use SAML claims.

 

So that’s how this all works.  It’s somewhat complicated, but also has lots of flexibility for configuration.  I’ve attached the Claims Provider Activation application to this posting so you can download and use it.  My disclaimer is that I’ve only tested it in my environment on my single server farm, so I will freely invoke the developer’s defense mantra of “but it works on my machine!”  However, if you try it and find it isn’t working for your particular scenario you can leave a comment and I will try when I have free time to take a look at it. 

 

You can download the attachment here: