App URL’s in SharePoint 2013

The SharePoint 2013 App Model introduces the concept of host named app webs. The host named app web is a sub web of the site collection that had an app installed – think of it as an app-specific storage location for an app instance. Each installation of an app can have an app-web. In order to help protect against XSS and CSRF attacks, these app-specific webs are actually hosted via a host name, as if they were site collections.

The highlighted bit is an appweb. Note that this list was returned by looking at the contents of the AllWebs property dangling off an SPSite.

App addressing is well covered on Mirjam van Olst’s blog here, as well as on TechNet. What’s not explicitly stated anywhere is the fact that you cannot use a short name for the app domain. This means that your app domain – the domain into which your apps are addressed – won’t by default find its way into Internet Explorer’s Intranet zone.

Why not? Well, the answer is quite simple, really. DNS suffix addition isn’t stipulated in the DNS protocol, it’s a convenience provided by the operating system, and the operating system by default doesn’t bother appending a DNS suffix if the hostname contains a ‘.’. What this means is that when you type in https://team/ in your web browser, Windows does you a favor by iterating through your DNS suffix list configured on your network adapter until it finds a name that resolves. But, https://foo.apps/ doesn’t get that special treatment – there is a ‘.’ in the name, and so Windows assumes that the name is fully-qualified and doesn’t bother with the DNS suffix list.

Since each app web gets an app web URL that is a subdomain of your app domain, all app webs wind up being of the form;

<prefix>-<appidentifier>.<appdomain>

As a result, if you specify a short name as your app domain, you wind up with calculated app web URLs that look something like (assuming an app domain of apps and an app prefix of foo):

foo-b9d4383e780155.apps

Which, since it contains a ‘.’ won’t get the normal DNS suffix love, and won’t resolve. And even if it *did* resolve, it wouldn’t get mapped to the local intranet zone in Internet Explorer in any case.

The bottom line: when selecting an app domain you (for all practical purposes) must use a fully-qualified domain name. Anything else is unlikely to work (without extraordinary shenanigans in your DNS server configuration). And that means that, if you are using Windows authentication, you’ll need to add your app domain to the local intranet zone in Internet Explorer to prevent folks using a SharePoint app from a second authentication.

Next time, I’ll talk a little bit about why Office 365 avoids this problem.

 

 

 

The Netbook Lives

I keep hearing people talking about the death of the netbook. Acer and Asus aren’t even manufacturing them anymore. Asus practically invented the form factor, and now they are walking out on it. Folks are saying that a lack of netbook sales is impacting Window 8 adoption.

Full Disclosure – I’m a former Microsoft employee and current Microsoft stock holder.
I do some consulting work with them from time to time. That said, these opinions are completely my own.

I got on a plane today, and after waiting to hit 10,000 feet, pulled out my Surface RT, signed on to the inflight WiFi, and RDP’d into my workstation back at home. After a few minutes,
I decided to unplug for a bit and watch a movie. (The Town, in case you were wondering. Masterful. Ben Affleck is an underappreciated treasure. I didn’t hate Jeremy Renner even once the whole movie – this is a feat for me.) Then I played a quick game of Angry Birds Star Wars, then read some notes in Evernote.

Then I noticed how well the Surface fits on the airplane. How after a 2.5 hour movie I still have > 70% battery left. How I’m banging this blog post in a full version of Word 2013. And I’m switching between the track pad, touching the screen, and typing on the keyboard with surprising fluidity, especially considering how in print that sounds like a huge usability miss. It’s not. It’s like having at your disposal all the tools you need.

The Netbook lives. It’s now called the Surface RT, and the only thing that makes me angry about it is the lack of Visual Studio.

Microsoft Certified Solutions Master: SharePoint

Today, my credentials look like this:

  • Microsoft Certified Master: SharePoint 2007
  • Microsoft Certified Master: SharePoint 2010
  • Microsoft Certified Solutions Master: SharePoint*

Note the asterisk on that last one. With the announcement at the SharePoint Conference that SharePoint 2013 is the last big-bang release planned, and that future releases would be more iterative in nature, the Microsoft Certified Master program needed to be revised to reflect this. Previously, once you earned your Master certification, you held it forever. Once a Microsoft Certified Master for SharePoint 2007, always a Microsoft Certified Master for SharePoint 2007 – the idea being, since the certification is tied to a version of the product, as the product ages your certification becomes less and less relevant. No recertification process required.

With more iterative product releases, this model breaks down. In response, Microsoft revised the certification programs and late last year debuted the Microsoft Certified Solutions Master: SharePoint certification. This certification is non-version specific, and requires recertification every three years.

Interestingly, they also grandfathered in all existing Microsoft Certified Masters for SharePoint 2010 into the program. The justification being that the new certification is non-version specific, and at the time of the certification programs release SharePoint 2010 was the most current in-market version of the product; SharePoint 2013 hadn’t shipped yet. There is one important caveat, though – this grandfathering is valid for a single year, as opposed to three. So, I’m an MCSM: SharePoint, but only for a year. I’ve got one year to upgrade my certification from MCM: 2010 to MCSM: SharePoint.

After working with the Microsoft Certified Solutions Master instruction team and Microsoft Learning for the last 6 months developing portions of the MCSM curriculum, I’m off to go attempt my upgrade by attending the Beta rotation of the MCSM: SharePoint program, and hopefully get that asterisk off my certification list.

It’s always so intimidating; the folks that attend the beta rotations (there are no alpha rotations, atleast not anymore) are some of the smartest, most talented, and most prolific folks in the SharePoint community. I’ll be the one in the corner trying not to make a fool of myself by asking a stupid question. :)

Blog Software

A few weeks ago, I had an epiphany. I’m a technology hoarder. This hoarding reflects my personal interests, and this translates itself into a form of snobbery.

  • I own not one, not two, but three Xboxes. I have zero PlayStations.
  • I own an iPhone and a Windows Phone 7, and I genuinely hate the iPhone.
  • I haven’t touched my iPad since I opened my Surface on Christmas morning.

Now I have justifications for all of the above, mind. Just not very good ones. I love the Surface. Love it. But I can’t honestly say it’s vastly superior to the iPad. It’s a competitive device, to be sure, but it’s not blowing the iPad out of the water or anything. And yet I use the Surface almost exclusively. Why? Because I’m biased in my personal technology choice.

For whatever reason, and I’m assuming that this isn’t uncommon, I see lots & lots of folks professionally focused on SharePoint who insist on running their blogs on SharePoint, and the same can be said of Orchard CMS, DotNetNuke, etc. It’s a weak argument for technology selection. Imagine if a toaster oven manufacturer decided to strap an engine to a toaster and drive it around as a car, solely because they happened to build toaster ovens. It’d be silly. Use the best tool for the job, right?

You could argue that if you are professionally invested in a content management technology platform, using that technology platform to solve your personal publishing needs is simply a way of demonstrating your ability to use the platform you specialize in. The problem with this is that blogs aren’t meant to demonstrate the blogging software, blogs are meant to publish content. Done properly the underlying platform in play should be barely noticeable (and some would argue that it shouldn’t be noticeable at all).

The design intent of many publishing packages meets with the needs of blogs to varying degrees. This is a result of tradeoffs in features/functionality that naturally occur in platform design. DotNetNuke can run blogs just fine, thank you, but to get all the bells of WordPress you are going to have some work to do – not because DotNetNuke is bad, per se, but because DotNetNuke was intended to solve a much more general problem space. Compare that to WordPress, which was intended to be a blogging platform first (although it can do far more than that now).

Ultimately, technology snobbery wound up causing me to pick a platform that kept me from blogging – not because I didn’t have interest, but because I was frustrated with my tools. I’ve switched blog engines a few times, but I’ve always restricted myself to .NET based engines, mainly because I worked for Microsoft for quite a while, worked in the SharePoint PG for a bit, and have spent the last half-decade focused entirely on the Windows ecosystem. I was being a technology snob.

I’ve decided to switch one more time, but this time I focused on functionality of blog platform as opposed to the technology stack it’s built in. WordPress is a joy to publish with, and I’m hoping and praying that this will be the last switch I ever make.

How To: Enable Site Mailboxes with SharePoint & Exchange

Editor’s Note: There is authoritative guidance on TechNet around configuring this that can be found at Configuring server-to-server authentication between SharePoint 2013 and Exchange 2013 and Configure Site Mailboxes in SharePoint Server 2013.

Pre-Requisites

Before we get started, there are a few things you need to have in place.

User Profile Service Application

You have to have a working user profile service application with profiles for your users that have at least one of the following properties populated:

Property Display Name Internal Name
SID SID
User Principal Name SPS-UserPrincipalName
Work email WorkEmail
SIP Address SPS-SipAddress

 

If you’ve got user profile synchronization configured & working against Active Directory, you should be all set. By default, the proper SharePoint profile properties are mapped to the correct Active Directory attributes. If, on the other hand, it’s not synchronizing successfully, or you are using a different directory to synchronize against (say, Novell eDirectory) you will need to map the above properties manually.

Why is this step needed? That’s another post, but suffice it to say if these properties aren’t in place, server-to-server authentication won’t work properly. You can find more details on the why at Server-to-server authentication and user profiles in SharePoint Server 2013.

Exchange Web Services Managed API

You can download this from the Microsoft Download Center at http://www.microsoft.com/en-us/download/details.aspx?id=35371. You need to install this on every WFE in your farm. Be warned – by default, the MSI does not install the necessary bits to the Global Assembly Cache, meaning SharePoint won’t be able to find or use the installed assemblies and site mailbox creation will fail as a result. Make certain you install this package from the command line, using this command:

msiexec /i EwsManagedApi.msi addlocal=”ExchangeWebServicesApi_Feature,ExchangeWebServicesApi_Gac”

Configuring SharePoint for Site Mailboxes

Now that the pre-requisites are out of the way, we can configure SharePoint & Exchange for site mailboxes.

Configuring the Trust Relationship on SharePoint

First, you must configure your SharePoint installation to trust your Exchange Server installation. You first need to determine the autodiscover domain of your Exchange server. You can get this from your Exchange administrator; generally speaking, the autodiscover address will take the form of autodiscover.<maildomain.com>, where <maildomain.com> is your email address domain. For example, let’s say your email address was bryan.porter@litware.com. It’s likely that your autodiscover domain is autodiscover.litware.com.

Once you have determined your autodiscover domain, you need to construct a URL to the service endpoint metadata that SharePoint will use to configure the trust relationship to Exchange. Exchange 2013 publishes a metadata document on its autodiscover domain that describes to servers and services that trust it how it will secure any tokens it issues – this information contains the signing key details, amongst other information.

In our example, if my autodiscover domain is autodiscover.litware.com, then the URL to the endpoint metadata document would be:

https://autodiscover.litware.com/autodiscover/metadata/json/1

It’s important to stress that this step is entirely dependent on how your Exchange server has been configured. For example, in my lab I’m running a single-server installation of Exchange 2013, in which case my URL is

https://lspex01/autodiscover/metadata/json/1

If you don’t have direct control over your Exchange server, find your Exchange administrator and ask them – they should immediately know what you are talking about! Determining if you’ve got the right URL is easy to check – simply copy the metadata URL you think is right into your browser and see if anything is returned at that address! If you see some cryptic text appear (it’s actually JSON-formatted endpoint configuration data), you should be good to go.

Once you’ve got your metadata URL worked out, open a SharePoint Management Shell with administrative privileges on a machine in your SharePoint farm. Execute the following commands:

$exch = New-SPTrustedSecurityTokenIssuer –MetadataEndpoint “<autodiscover url>” –Name “<a friendly name>”

$exchApp = Get-SPAppPrincipal -Site https://<URL> -NameIdentifier $exch.NameId

Set-SPAppPrincipalPermission -AppPrincipal $exchApp -Site https://<URL> -Scope SiteSubscription -Right FullControl -EnableAppOnlyPolicy

…where <autodiscovery url> is the URL you determined earlier, <a friendly name> is just a name that’s easy for you to remember and identify later, and <URL> is the URL of a SSL protected site collection. As an example, in my lab environment, I would type & execute the following:

$exch = New-SPTrustedSecurityTokenIssuer –MetadataEndpoint “https://lspex01/autodiscover/metadata/json/1″ –Name “Exchange 2013″

$exchApp = Get-SPAppPrincipal -Site https://team -NameIdentifier $exch.NameId

Set-SPAppPrincipalPermission -AppPrincipal $exchApp -Site https://team -Scope SiteSubscription -Right FullControl –EnableAppOnlyPolicy

Why do you need an SSL protected web application? The details will have to wait for another post, but the short version is that it’s all about protecting something called the OAuth2 Bearer Token from exposure. Suffice it to say, this token is critical component of server-to-server authentication. It *is* possible to configure this without using an SSL protected web application, but that configuration would only ever be appropriate for lab or development purposes – never production.

If you receive an error complaining that “The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.“, this just means that the SSL certificate being used to protect your autodiscover endpoint (https://lspex01/, in my second example) isn’t trusted. This could have been done purposefully by your organization. The fix is simple & straightforward – you’ll need to trust the certificate used by Exchange. You can do this by opening an instance of Internet Explorer and navigating to the URL you determined earlier. You’ll likely receive a warning about the certificate that’s in use – this is the same certificate check that’s causing grief, so that’s expected. Once the metadata document appears on your screen, click the ‘Certificate error’ message in your browser bar and click ‘View verticates’ to pull up the details of the certificate. On the dialog box that appears, click ‘Install Certificate’, choose ‘Local Machine’ as the store location. On the ‘Certificate Store’ page, pick ‘Place all certificates in the following store’ and click ‘Browse’, selecting ‘Trusted Root Certification Authorities’. Click ‘OK’ to close the ‘Select Certificate Store’ dialog, and the click ‘Next’ on the wizard. On the wizard summary page, click ‘Finish’. If all goes well, you should receive a ‘Import successful’ message.

This all assumes a fairly basic certificate configuration for your Exchange server. If you’ve got a custom certificate authority issuing these certificates that’s untrusted by your SharePoint servers, the exact steps might be different – you might have more certificates to trust using the above procedure, for example. Your Exchange administrator should be able to help you understand how things are configured. If your Exchange server is using an SSL certificate from a global SSL provider, this step won’t be required. Regardless, if you get the error message above, your issue is that the SSL certificate isn’t trusted by your SharePoint server.

Note: Some folks have told me that adding the certificate to SharePoint’s Trusted Root Authority list is also needed. That’s baloney. The New-SPTrustedSecurityTokenIssuer cmdlet doesn’t consult that store when verifying certificates; in fact, it doesn’t do anything other than whatever verifications the .NET platform does by default. That’s why trusting the certificate via Internet Explorer makes this issue go away – the underlying operating system component here is WinHTTP, and nothing else.

Configuring the Trust Relationship on Exchange

Now that SharePoint trusts Exchange, we need to configure Exchange to trust SharePoint.

On your Exchange server, open an Exchange Management Shell window. Then, change the working directory to ‘C:\Program Files\Microsoft\Exchange Server\V15\Scripts\’, and run the following command:

.\Configure-EnterprisePartnerApplication.ps1 -AuthMetadataUrl https://<URL>/_layouts/15/metadata/json/1 -ApplicationType SharePoint

where <URL> is the URL of an SSL protected web application.

Configuring SharePoint for Site Mailboxes

At this point, we’ve got a functioning User Profile service application, we’ve got the Exchange Web Services Managed API’s installed on our SharePoint web front-ends, and we’ve got Exchange and SharePoint trusting one another. Now, we need to tell SharePoint about a few Exchange endpoints it will need in order to communicate with Exchange properly.

Each SharePoint web application that will house site collections that can request site mailboxes need to have two values placed in the property bag associated to the web application. These two properties are known as ‘ExchangeTeamMailboxDomain’ and ‘ExchangeAutodiscoverDomain’. The ‘ExchangeTeamMailboxDomain’ is just the SMTP domain that site mailboxes will use. For example, if all your site mailboxes should be ‘@sites.litware.com’, you’d put ‘sites.litware.com’ here. You place here what you want to be in after the @ sign in your site mailbox email address. Note: your Exchange server has to also be configured to accept and manage mail for this domain, so don’t go putting something here that Exchange isn’t already configured to use. You should work with your Exchange administrator to select a domain that’s appropriate for your organization.

The second bit is the ‘ExchangeAutodiscoverDomain’. The good news is, we already know just what to put here – since we had to figure out the autodiscover domain to use earlier. For example, if the URL you determined earlier for your Exchange metadata endpoint wound up being:

https://autodiscover.litware.com/autodiscover/metadata/json/1

then you’ll set ‘ExchangeAutoDiscoverDomain’ to

autodiscover.litware.com

Execute the following commands from within a SharePoint Management Shell:

$webApp = Get-SPWebApplication <URL>

$webApp.Properties["ExchangeTeamMailboxDomain"] = “<mailbox domain>”

$webApp.Properties["ExchangeAutodiscoverDomain"] = “<autodiscover domain>”

$webApp.Update()

where <URL> is the URL of the SSL protected web application we’ve been using all along, <mailbox domain> is the domain that all site mailboxes will use as their ‘@’ address, and <autodiscover domain> is the domain we used earlier to fetch the metadata document for Exchange. The $webApp.Update() line just makes sure it’s all saved to the SharePoint Configuration database.

At this point, we only have to enable the site mailbox farm-level feature. You can do that via Central Administration, in System Settings – look for ‘Manage Farm Features’. Or, you can do it via PowerShell by executing the following commands from within a SharePoint Management Shell:

Enable-SPFeature -Identity CollaborationMailboxFarm

Trying It Out

Navigate to a site collection on the SSL protected web application you’ve been using. If you’ve still got the ‘Getting started with your site’ web part displayed on the site homepage, you should see a ‘Keep email in context’ action – click that and follow the prompts. Otherwise, navigate to Site Settings, choose ‘Manage site features’ from the ‘Site Actions’ group, and enable the ‘Site Mailbox’ feature. After the feature is activated, navigate to site contents – click the little gear icon in the upper right hand corner of the web page, and choose ‘Site contents’ from the menu, or from the left-hand “quick launch” menu – and click ‘Site Mailbox’ in the ‘Lists, Libraries, and other Apps’ group. Follow the on-screen instructions, and you should get a notification that your site mailbox is being provisioned.

A Few Points

If anything goes wrong, the Site Mailbox app will give you an error message that also contains an error code. Check the error code against the table at http://technet.microsoft.com/en-us/library/jj552524.aspx to determine exactly what the problem is. I’ve encountered problems enabling site mailboxes due to forgetting to do basic things; things like forgetting to set the ExchangeTeamMailboxDomain property, misspelling the name of the ExchangeAutodiscoverDomain property, doing other goofy things like specifying a team mailbox domain that Exchange wasn’t configured for, etc. In all cases, the error code led me to water in terms of finding a solution.

How To: Update Site Collection Quotas For Existing Site Collections

Setting the default quota template for site collections in a web application is easy enough. But what do you do when you want to change the template associated with some or all of the site collection in a web application? Out of the box, there are no good tools to do this. While we certainly have a UI to manipulate the quota template associated to a site collection, it’s a single-site-collection-at-a-time affair – a daunting task when you’re dealing with more than even a few site collections.

Take the example of My Sites. When planning your My Site deployment, you’ll probably spend good bit of time planning out the storage neccessary to support your end user adoption rate of My Sites, and also spend a fair bit of time discussing how much space (maximum) you want to allow your users to consume. You’ll enforce this with a template. But what happens in 6 months, when you decide that your estimates were too high, or too low, and you need to either increase the storage that end users can use, or decrease the storage? Manually adjusting 1000′s of site collections would take days!

Enter a wee bit of PowerShell script and the SharePoint 2010 Management Shell. We have to use a few bits of the SharePoint object model to make this work, but this task is simple enough to be approachable by even the most code-averse IT pro’s out there:

# Replace all site collection quotas in a web application with a new template
$TemplateName = "My Template Name"
$WebApplicationUrl = "http://my/"

$contentService = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
$quotaTemplate = $contentService.QuotaTemplates[$TemplateName]
$webApplication = Get-SPWebApplication $WebApplicationUrl
$webApplication.Sites | ForEach-Object { try { $_.Quota = $quotaTemplate; } finally { $_.Dispose(); } }

 In the above, just replace “My Template Name” with the name of the new or updated quota template you’d like to assign, and http://my/ with the URL of the web application you’d like updated. This script will replace the quota template on every site collection within that web application.

Too heavy handed for you? What if you only wanted to replace the template assigned to a site collection if it was already assigned a specific template? So, for instance, you’ve got some sites using a quota template called “Bad Template”, but you don’t want to use that template anymore, and you want to replace the quota templates assigned to those folks with a new template, “Good Template”. What to do?

Enter a nother wee bit of script:

# Replace all site collections having a specific quota template with a new quota template
$OldTemplateName = "Bad Template"
$NewTemplateName = "Good Template"
$WebApplicationUrl = "http://my/"

$contentService = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
$quotaTemplate = $contentService.QuotaTemplates[$OldTemplateName]
$replaceQuotaTemplate = $contentService.QuotaTemplates[$NewTemplateName]

$webApplication = Get-SPWebApplication $WebApplicationUrl

$webApplication.Sites | ForEach-Object { try { if ($_.Quota.QuotaID -eq $quotaTemplate.QuotaID) { $_.Quota = $replaceQuotaTemplate } } finally { $_.Dispose(); } }

Again, simply replace “Bad Template” with the template you want to replace, “Good Template” with the name of the new template to replace “Bad Template” with, and http://my/ with the URL of the web application you want to process.

But wait! What if you don’t want *any* quota templates! How do I remove the quota templates from all sites with a specific quota template already applied? Easy enough:

# Replace all site collections having a specific quota template with a new quota template
$OldTemplateName = "Bad Template"
$WebApplicationUrl = "http://my/"

$contentService = [Microsoft.SharePoint.Administration.SPWebService]::ContentService
$quotaTemplate = $contentService.QuotaTemplates[$OldTemplateName]

$webApplication = Get-SPWebApplication $WebApplicationUrl

$webApplication.Sites | ForEach-Object { try { if ($_.Quota.QuotaID -eq $quotaTemplate.QuotaID) { $_.Quota = $null } } finally { $_.Dispose(); } }

Hope that helps those of you struggling with ex-post-facto updates to site collection quotas!

Adding users and claims to a site from PowerShell

Update: Astute readers will note that the following examples make use of the scratch custom claims provider.

Recently, the question of automating custom claim assignments to SharePoint groups has come up. For example, if you’ve got a custom claim provider that provides a custom claim of type http://schemas.bryanporter.com/favoriteColor, how could you add all users that present that claim to your farm into the Visitors group of a SharePoint site?

Managing users is pretty straightforward. So, if you had a user CONTOSO\brporter that already existed in a SharePoint site, you could simple run the following PowerShell to add them to the site and assign them to the Team Site Visitors group:

1 # If the user already exists in the site…2 $user= Get-SPUser -Identity (New-SPClaimsPrincipal -Identity CONTOSO\brporter -IdentityType WindowsSamAccountName).ToEncodedString() -Web http://<web site URL>3 Set-SPUser -Identity $user-Web http://<web site URL>-Group Team Site Visitors

If your user was new to the site, replace Get-SPUser with New-SPUser and you are all set:

1 # If the user is new to the site2 $user= New-SPUser -UserAlias (New-SPClaimsPrincipal -Identity CONTOSO\tuser -IdentityType WindowsSamAccountName).ToEncodedString() -Web http://<web site URL>3 Set-SPUser -Identity $user-Web http://<web site URL>-Group Team Site Visitors

But what about a claim? What if you only want to allow users that present, say, a favoriteColor claim with a value of Blue to be able to visit a particular site? To make sure that ardent adorers of the color Blue can visit your site:

1 # If you are securing based on a claim2 $claimProvider= Get-SPClaimProvider | where { $_.DisplayName -eqThe Name Of My Claim Provider} 3 $claim= New-SPClaimsPrincipal -ClaimValue Blue -ClaimType http://schemas.bryanporter.com/favoriteColor -ClaimProvider $claimProvider.ClaimProvider 4 5 $user= New-SPUser -UserAlias $claim.ToEncodedString() -Web http://<web site URL>6 Set-SPUser -Identity $user-Web http://<web site URL>-Group Team Site Visitors

The non-obvious bit here is that we continue to deal with New-SPUser, even when we’re actually talking about a custom claim.

I should also point out that unless your custom claim provider successfully resolves the claim value and type you won’t get a claim reference that can return a proper encoded string – effectively preventing you from securing on the claim. For more infromation on implementing claim resolution, see Steve Peschka’s most excellent walkthrough on MSDN.

The Scratch Custom Claims Provider

Frequently, I find it useful to have a common starting point – what Raymond Chen describes as a “scratch program” (or a new scratch program, for those update-minded types). Within SharePoint, things are never that simple – a scratch SharePoint solution is an empty whiteboard, for better or for worse.

There are plenty of extensibility points within SharePoint that certainly offer up opportunities for scratch programs, though, and one place I find myself sharing – over and over again – my personal scratch implementation is with Custom Claims Providers. And so I submit for your approval, the SimpleClaimsProvider in all its favoriteColor issuing claim glory. This will serve as a starting point for future posts involving custom claims providers, and in the meantime might give a few of you the bits you need in order to make the details of Custom Claims Providers real. For a complete walkthrough, see Steve Peschka’s most excellent write up over on MSDN.

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 using Microsoft.SharePoint; 7 using Microsoft.SharePoint.WebControls; 8 using Microsoft.SharePoint.Administration; 9 using Microsoft.SharePoint.Administration.Claims; 10 11 namespace BryanPorter.SP.CCP 12 { 13 public class SimpleClaimsProvider 14 : SPClaimProvider 15 { 16 private static string m_claimValueType = Microsoft.IdentityModel.Claims.ClaimValueTypes.String; 17 private static string m_claimType = "http://schemas.bryanporter.com/favoriteColor"; 18 19 private string m_sharedState = null; 20 21 public SimpleClaimsProvider(string displayName) 22 : base(displayName) 23 { } 24 25 protected override void FillClaimTypes(List<string> claimTypes) 26 { 27 if (claimTypes == null) 28 throw new ArgumentNullException("claimTypes"); 29 30 claimTypes.Add(m_claimType); 31 } 32 33 protected override void FillClaimValueTypes(List<string> claimValueTypes) 34 { 35 if (claimValueTypes == null) 36 throw new ArgumentNullException("claimValueTypes"); 37 38 claimValueTypes.Add(m_claimValueType); 39 } 40 41 protected override void FillClaimsForEntity(Uri context, SPClaim entity, List<SPClaim> claims) 42 { 43 if (entity == null) 44 throw new ArgumentNullException("entity"); 45 46 if (claims == null) 47 throw new ArgumentNullException("claims"); 48 49 claims.Add(CreateClaim(m_claimType, "Blue", m_claimValueType)); 50 } 51 52 protected override void FillEntityTypes(List<string> entityTypes) 53 { 54 entityTypes.Add(SPClaimEntityTypes.FormsRole); 55 } 56 57 protected override void FillHierarchy(Uri context, string[] entityTypes, string hierarchyNodeID, int numberOfLevels, Microsoft.SharePoint.WebControls.SPProviderHierarchyTree hierarchy) 58 { 59 throw new NotImplementedException(); 60 } 61 62 protected override void FillResolve(Uri context, string[] entityTypes, SPClaim resolveInput, List<Microsoft.SharePoint.WebControls.PickerEntity> resolved) 63 { 64 if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole)) 65 return; 66 67 PickerEntity pe = CreatePickerEntity(); 68 pe.Claim = CreateClaim(m_claimType, "Blue", m_claimValueType); 69 pe.Description = Name + ":" + "Blue"; 70 pe.DisplayText = "Blue"; 71 pe.EntityData[PeopleEditorEntityDataKeys.DisplayName] = "Blue"; 72 pe.EntityType = SPClaimEntityTypes.FormsRole; 73 pe.IsResolved = true; 74 pe.EntityGroupName = "Favorite Color"; 75 76 resolved.Add(pe); 77 } 78 79 protected override void FillResolve(Uri context, string[] entityTypes, string resolveInput, List<Microsoft.SharePoint.WebControls.PickerEntity> resolved) 80 { 81 if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole)) 82 return; 83 84 if (resolveInput.ToUpper().Contains("BLUE")) 85 { 86 PickerEntity pe = CreatePickerEntity(); 87 pe.Claim = CreateClaim(m_claimType, "Blue", m_claimValueType); 88 pe.Description = Name + ":" + "Blue"; 89 pe.DisplayText = "Blue"; 90 pe.EntityData[PeopleEditorEntityDataKeys.DisplayName] = "Blue"; 91 pe.EntityType = SPClaimEntityTypes.FormsRole; 92 pe.IsResolved = true; 93 pe.EntityGroupName = "Favorite Color"; 94 95 resolved.Add(pe); 96 } 97 98 } 99 100 protected override void FillSchema(Microsoft.SharePoint.WebControls.SPProviderSchema schema) 101 { 102 schema.AddSchemaElement(new SPSchemaElement(PeopleEditorEntityDataKeys.DisplayName, "Display Name", SPSchemaElementType.Both)); 103 } 104 105 protected override void FillSearch(Uri context, string[] entityTypes, string searchPattern, string hierarchyNodeID, int maxCount, Microsoft.SharePoint.WebControls.SPProviderHierarchyTree searchTree) 106 { 107 if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.FormsRole)) 108 return; 109 110 if (searchPattern.ToUpper().Contains("BLUE")) 111 { 112 PickerEntity pe = CreatePickerEntity(); 113 pe.Claim = CreateClaim(m_claimType, "Blue", m_claimValueType); 114 pe.Description = Name + ":" + "Blue"; 115 pe.DisplayText = "Blue"; 116 pe.EntityData[PeopleEditorEntityDataKeys.DisplayName] = "Blue"; 117 pe.EntityType = SPClaimEntityTypes.FormsRole; 118 pe.IsResolved = true; 119 pe.EntityGroupName = "Favorite Color"; 120 121 122 searchTree.AddEntity(pe); 123 } 124 } 125 126 public override string Name 127 { 128 get { return "SimpleCCP"; } 129 } 130 131 public override bool SupportsEntityInformation 132 { 133 get { return true; } 134 } 135 136 public override bool SupportsHierarchy 137 { 138 get { return false; } 139 } 140 141 public override bool SupportsResolve 142 { 143 get { return true; } 144 } 145 146 public override bool SupportsSearch 147 { 148 get { return true; } 149 } 150 } 151 }

How often will FillClaimsForEntity in my Custom Claims Provider be called?

This was the question I posed to a colleague this morning. His answer surprised me – “Every time you authenticate.”

Before we move on, let’s just call this foreshadowing.

Problem is, this statement was at odds with the behavior that I, and many of you, were seeing/have seen. It just didn’t seem to be true. Clearly, I was seeing that very nearly every single time a page load occurred, my custom claims provider was invoked. Looking at a Fiddler trace, it was also obvious that I wasn’t receiving a 401 on every page request, either. This was certainly different than a web application configured for Windows Classic authentication – where you can sometimes see a 401 on every page load.

First, a refresher on the oddities of a claims based web application.

When a web application is configured for claims based authentication, the IIS binding has Anonymous access enabled on it. This is because, unlike with Windows Classic, authentication is a SharePoint problem with claims (SAML or Windows). SharePoint 2010 manages this via a module (or two) within the IIS pipeline. The enablement of anonymous access generally goes unnoticed by most administrators, since its one of a myriad of settings that the product sets on the IIS binding for a web application. My personal experience is that when administrators do notice this, they generally freak out. It’s okay. The product did this on purpose; if it didn’t you wouldn’t have all that fancy claims-based authentication federating with your partner ADFS instances and delegating the pain of user management. This is a feature.

Refresher over.

So, why do I see a ton of 401’s when running under Windows Classic, but only one, at session start, under Windows Claims? And how does this influence the frequency with which my custom claims provider is going to get called?

image
401’s with Windows Classic

Under Windows Classic, IIS is in charge of authentication. And IIS says that if a request doesn’t include an Authorization header, it is challenging for credentials based on the configuration of the site. Therefore, you’ll see a lot more 401’s under Windows Classic. Now, a 401 is expensive. With NTLM authentication, every 401 means that an Active Directory Domain Controller was interrogated to validate the passed credentials.

When you’ve got a web application that is configured for Windows Claims only, and Windows Claims is the only authentication type specified for that web application, you execute a very unique code path within the authentication system. If you run a Fiddler trace you’ll notice that after the initial 401, a cookie, WSS_KeepSessionAuthenticated, is set on the browser. This cookie is an index into a token cache maintained by SharePoint, and is actually a chunk of code left over from SharePoint 2007 that is only invoked when a site is:

  1. Configured for Claims Based Authentication
  2. Only configured for Windows Claims (i.e. NTLM or Kerberos authentication)

Remember that integrated mode module that we talked about before? The one that handles authentication for claims based web applications? When this module sees the WSS_KeepSessionAuthenticated cookie, it checks the state of the cached token that the cookie value points to on the server, and in so doing executes a code path that iterates over all registered claim providers and, as a result, winds up calling FillClaimsForEntity on each one that returns true from SupportsEntityInformation (For more information, see the SPClaimProvider documentation on MSDN).

We’ll cache the token according to settings specified on the security token services configuration (accessible via PowerShell by calling Get-SPSecurityTokenServiceConfig). As a side benefit, we don’t wind up issuing a 401 on ever page request in this model, significantly reducing authentication overhead and domain controller impact, but we do wind up calling any registered claim providers a lot. Lesson: Optimize your claim providers – slow downs in authentication will equal a slow site.

Now, add a second authentication mechanism, and things suddenly change. Under Windows Claims, if a second authentication method is defined for the URL – say, Forms, or a Trusted Identity Provider – we stop issuing the WSS_KeepSessionAuthenticated cookie, and start issuing a FedAuth cookie instead. The FedAuth cookie is the the cookie we issue when you are using SAML Claims, FBA, or Windows Claims with multiple authentication providers. The details of authentication when a FedAuth cookie is used are significantly different; for instance, we’ll only call registered claim providers when an actual FedAuth cookie is issued, as that’s the time authentication really occurs; we’ll only re-execute registered claim providers when the cookie expires and an authentication is forced again.

All this talk of Windows Authentication and cookies might have some folks wondering – cookies can be a real pain, I went with Windows Authentication to avoid cookies and the pain they bring. Remember the death by a thousand cuts that was the integration  of Office 2007 and SharePoint FBA applications under SharePoint 2007 before Service Pack 2 (and a litany of other conditions)?

Using cookies to streamline the authentication process is actually okay with Windows Authentication, because the browser first authenticated with Windows Auth, and will respond seamlessly if it receives a 401 down the road. The fact that it’s not getting 401’s mean faster browsing for you, less chatter between the server and the domain controller infrastructure (in the case of NTLM authentication), etc. Should a 401 be issued, the client will know just what to do.

Remember earlier, when my colleague tried telling me that my custom claims provider will have its FillClaimsForEntity called on every authentication? Turns out, he was right – every time I authenticate, even if the authentication process is short circuited (ala WSS_KeepSessionAuthenticated and Windows Claims), my custom claims provider will be invoked. Sometimes, though, I authenticate a lot less than I thought I would!

I hope this helps those folks that are trying to understand the complexities behind custom claims providers and the logic behind their invocation.

Configuring Failover Databases – and Removing That Configuration

SharePoint 2010 supports database mirroring for just about every database you can think of (there are some exceptions – the Synchronization database of the User Profile service, for instance). This allows SharePoint to make use of the Failover Partner attribute within the connection to specify the failover server to the underlying data access stack, making use of a failover database seamless. We just use the plumbing the data access (ADO.NET for the most part, and the components of its stack) provides, and get to take advantage of Other Peoples Code that just Does The Right Thing.

Once you’ve created a database, though, going back and adding a failover partner can only be done via PowerShell – the Central Administration UI is disabled for this purpose. This is easy enough to do:

1 # Set failover database server 2 $database = Get-SPDatabase | where { $_.Name -eq "SomeSharePointDB" } 3 $database.AddFailoverServiceInstance("FailoverSQLServerName\OptionalInstanceIfUsed") 4 $database.Update();

What’s less obvious is what you need to do when you want to turn things off. For instance, when you are adjusting your mirroring configuration and need to replace (or even remove altogether) the failover partner for a time. If you are just changing the name/instance of the failover partner, you can just re-run the script above to adjust the failover partner server name. If, on the other hand, you are removing the failover partner entirely, you’ll need to execute the above script but specify either the PowerShell special variable $null as the failover partner server name, or an empty string – either will work. In so doing, you’ll remove the failover server configuration for that database, and SharePoint will adjust the connection string it uses when connecting to that database, removing the Failover Partner attribute.