Monthly Archives: November 2011

Ensure an SPUser Exists in an Application Page Within an Anonymously Accessible Site

Posted by Danny Jessee on November 20, 2011
SharePoint / 2 Comments

I was recently confronted with a requirement to develop an application page that could retrieve information about the currently logged in user from an external system based on that user’s email address. Piece of cake, I thought: I would just use the SPContext.Current.Web.CurrentUser object, which contains a string property called Email containing that SPUser‘s email address. I would take that string, pass it to the external system, get my information back, and call it a day. There was only one problem: the application page had to run within a site that had anonymous access enabled, and none of the users who would be accessing the page were explicitly assigned any permissions within the site.

A quick aside about anonymously accessible application pages

The default “Application Page” SPI within Visual Studio 2010 creates a page that inherits from the LayoutsPageBase class. A frequent complaint I hear is that an application page is prompting a user to log in even though that page is being accessed from within the context of an anonymously accessible site. If I had wanted my application page to be anonymously accessible, I would have to make the following tweaks to what Visual Studio 2010 gives me by default:

  • First, ensure that anonymous access is enabled for the Entire Web Site. I would do this from Site Actions > Site Permissions in SharePoint.
  • Change the page inheritance so that it inherits from the UnsecuredLayoutsPageBase class (shown below).
  • Explicitly override the AllowAnonymousAccess property and set its value to true (shown below).

But back to the problem at hand…

Since I want to force users to log in to access my application page (even with anonymous access enabled at the site level), I will leave the default base class of LayoutsPageBase. Can anyone see what problem might arise with my scenario?

It turns out that since none of the users accessing the page were explicitly assigned any permissions within the site (nor belonged to any site groups), SPContext.Current.Web.CurrentUser was returning null!

Fortunately, there is some good news here: even though the SPContext may not know who the current user is, the System.Web.HttpContext does! The property this.Context.User.Identity.Name contains the login name of the current user, which I can then pass to the SPWeb.EnsureUser() function to create the SPUser in the given SPWeb based on that login name. The EnsureUser() function returns an SPUser object that is populated with the email address of the user (along with the other properties of the SPUser object such as LoginName, Name, ID,
etc.)

NOTE: Do NOT try to call SPWeb.EnsureUser() in the context of the currently logged in user. It is important to run this code inside a RunWithElevatedPrivileges() delegate because the System Account will have the necessary permissions to add the specified user to the SPWeb. After all, if the specified user already had permission to do this, we wouldn’t need to be adding him or her in the first place!

The final code to accomplish everything (with proper error checking and handling omitted for brevity) looks like this:

Note that I don’t wrap the entire method with a RunWithElevatedPrivileges() delegate or web.CurrentUser would return the identity of the System Account, which is definitely not what I want.

Has anyone else encountered a similar scenario and solved the problem differently? Please feel free to discuss in the comments below!

Tags: , , , , ,

Fun with BCS, External Lists, and PowerShell!

Posted by Danny Jessee on November 15, 2011
SharePoint / No Comments

A recent question on SharePoint Stack Exchange inspired me to want to learn more about interacting with SharePoint lists using PowerShell, especially external lists created through Business Connectivity Services (BCS) in SharePoint 2010. With my background being more in .NET development using the object model, I was surprised to discover some of the inconsistencies one sees when interacting with an external list in PowerShell (compared with a standard out-of-the-box list).

For the purposes of this discussion, I will assume that you are familiar with how to use SharePoint Designer to create an external content type mapped to a SQL Server data source. From there, I created an external list for this external content type.

What initially made my learning journey such a confusing one was that the SQL Server table I used as the data source for my external content type contained a column named ID of type integer. It is possible that any external line-of-business (LOB) data source you consume through BCS may have a similar column as well. The values I initially inserted into the database table had auto-increment values of 1, 2, 3, etc. This enabled me to interact with the items in my external list using the following PowerShell syntax:

The first two lines may be unfamiliar to those of you who have not programmed with BCS before. With BCS, it is necessary to establish an SPServiceContext within an SPServiceContextScope that represents the HTTP context of the Business Data Connectivity Service.

If you ever see the following error:

“The shim execution failed unexpectedly – Proxy creation failed. Default context not found.”

Make sure you instantiate an SPServiceContext within an SPServiceContextScope as I have above.

It appears that there are some things you can do without properly obtaining the SPServiceContext within an SPServiceContextScope (such as enumerating the Fields collection of the external list). That said, the safest approach appears to be to always instantiate the SPServiceContextScope as shown above. If anyone can shed any additional light on why this is, please feel free to share in the comments below!

Fun Fact: An ID is not always an ID!

Here is my external list, viewed through the browser:

As you can see, the list contains an ID column with integer values, mapped directly to the similarly configured column in my SQL Server database table:

This allows me to run the following script to obtain access to the item with an ID value of 2:

Seems reasonable, right? After all, the second item we insert into a new out-of-the-box SharePoint list would be assigned an ID value of 2 as well (without us having to do anything to make it happen). And since I was able to call GetItemById(2), that must mean the ID property of the list item is 2, right?

Wrong!

Well, this is interesting. It turns out that my call to GetItemById(2) only works for the following reasons:

  • My external list contains a column named ID.
  • That column is of data type integer.

For instance, I cannot create another external list mapped to a data source with a column named ID but of a different type (i.e., nchar(10)), even if that column only contains integer values. Here’s what happens in that case:

Of course, it goes without saying that for an out-of-the-box list, the behavior is as you would expect:

Another item of interest is that the ItemCount property of external lists always returns 0, no matter how many items are in the list:

If you have any fun stories or observations about dealing with external lists in PowerShell, please feel free to share them in the comments below!

Tags: , , , , , ,

Using Claims Authentication to Configure Multiple Authentication Providers in a CloudShare Environment

Posted by Danny Jessee on November 12, 2011
CloudShare, SharePoint / No Comments

This post originally appeared in the CloudShare Community Blog on October 25th.

CloudShare’s very own Chris Riley recently shared an environment of mine in a blog post about network orchestration. This environment leverages some exciting new capabilities in SharePoint 2010: namely Claims Authentication in a SharePoint 2010 web application and the ability to configure multiple authentication providers in a single zone. These technologies offer exciting prospects to anyone who previously had to configure separate host headers and URLs for different extranet customers, depending on the mechanism required for each subset of users to log in.

How does this look to end users? To see Claims in action, fire up the “SharePoint 2010 (Clean Installation)” image from the shared environment, open Internet Explorer and navigate to http://intranet.contoso.com.

You’ll see an empty Team Site with a “Claims Web Part” at the top. Since anonymous access to the site is enabled, the Claims Web Part will initially be empty.

Press the “Sign In” link at the top right of the page. The page that appears, http://intranet.contoso.com/_login/default.aspx, is new in SharePoint 2010 and allows users to choose from all the different authentication providers configured for that web application. (As a side note, there are some exciting opportunities for custom code development here, including the ability to automatically redirect users to a specific authentication provider based on parameters such as their IP address, bypassing this page of options entirely! After all, not all end users are going to appreciate the distinction between “Windows Authentication” and “Forms Authentication.”)

In this web application, I have configured the following providers:

  • Windows Authentication – standard NTLM credentials.
  • Forms-Based Authentication (FBA) – you may remember this option from MOSS. In this environment, I have created a custom membership provider that validates inputted credentials against a SQL Server database. In practice, FBA can be used to authenticate external users against Active Directory or any other account database.
  • Azure Access Control Services (ACS) v2 – allows users to log in with credentials from Open ID providers such as Windows Live ID and Facebook.

For a more in-depth read about how to configure Azure ACS v2 as an authentication provider in SharePoint, including adding other Open ID providers such as Google and Yahoo, check out this blog post by Travis Nielsen.

If I choose Azure ACS v2 (this can be given a friendlier name when you set it up as a Trusted Identity Provider through PowerShell), I will see the following screen. Claims Authentication relies on a series of HTTP redirects to seamlessly direct users between SharePoint and external trusted identity providers such as Azure ACS to log them in. This page is hosted completely outside of my SharePoint environment:

By choosing “Windows Live ID,” I will be redirected to https://login.live.com, where I will be prompted to enter my Windows Live ID credentials. (Similarly, you will be redirected to any of the other Open ID providers’ sites should you choose them instead.) After I sign in, another series of HTTP redirects takes place that eventually lands me back in my SharePoint environment, all logged in. The Claims web part on the page shows the various Claims that were sent by the Trusted Identity Provider in an XML-based Security Assertion Markup Language (SAML) token back to SharePoint:

As an application developer, I can leverage these Claims (you see some examples of Claims in the screenshot above include nameidentifier, emailaddress, userid, name, etc.) to make various authorization (what resources may this authenticated user access) and personalization (how is this particular user or class of user’s experience customized) decisions about how to handle this user.

Claims-based authentication and Azure ACS offer exciting possibilities to application developers who are liberated from having to maintain (or even worse, design and develop!) a user management system and all the nightmares that go along with it (think about password resets, forgotten passwords, security requirements for maintaining account information, etc.) As Chris mentions in his blog post, the same paradigm can be applied using Active Directory Federation Services (AD FS v2) to support users logging in to a SharePoint environment using credentials from a trusted external domain.

That said, Claims-based authentication is not always as easy to set up and work with as it may seem. Using CloudShare, developers and IT professionals can focus their valuable time and energy on solving the issues surrounding the implementation of Claims-based identity in SharePoint 2010 and not be concerned with software licensing, hardware, or other infrastructure concerns.

Tags: , , , , , ,