Archive

Archive for February, 2008

Toronto CodeCamp is only ONE day away!

February 29th, 2008 No comments

In just less than 32 hours , the Toronto CodeCamp 2008 will be held. Over 700 people have registered so far and registration is still open. If you haven’t registered yet ,you’d better hurry! This is an amazing opportunity for learning and networking with peers and industry leaders. Unfortunately, Eli Robillard (Toronto’s Uber SharePoint MVP) has taken a break as he has been so busy these days, but no worries – both myself and Bill Brockbank will be representing the SharePoint community along with bunch of other *great* speakers. I’ll be presenting one session:

Delving into custom authentication providers in SharePoint (02:30 PM – 03:45 PM Room: E.S. Jackson)

For those of you who attended this session in SharePoint user group , because I love you so much and I’d like to see you over and over again , I am coming with a fairly new presentation. I have managed to change the background of my PowerPoint deck, added couple new pictures (Just kidding) LOL …… In all seriousness , I have added the following topics to the previous presentation and got rid of bunch of slides.

  1. Utilities that must be shipped with any FBA implementations (These are NOT the tools we already talked about)
  2. Anonymous Access Challenges
  3. Single Sign ON with other ASP.NET apps
  4. MySites (If time)
  5. A new State Provider that I have just finished developing it (If time)

I’m telling you now that it is going to be a fast-paced session , because my mantra is : To “Share” as many “Points” as possible I also don’t like unreal demos (a.k.a Hello World! – Copied and pasted from someone else’s demo) .You don’t need to listen to my funny accent for those stuff, right?;)As always, I will be showcasing one of my previous projects.

See you on Saturday, March 1.

Categories: General, MOSS 2007, UG/CodeCamp Tags:

Central Admin Can’t See My Authentication Provider!

February 22nd, 2008 4 comments

Received today from CodePlex:

“Hi Reza, I have created a custom membership provider for my MOSS site [ it checks creds against a propriteray store]. I have implemented these methods in the CustomProviderClass public class MyCustomProvider:MembershipProvider
* GetAllUsers
* ValidateUser
* GetUser
* FindUsersByName
I followed the technet links to register the custom provider in the sharepoint Admin site as well as my custom website on which I would like to have FBA. Unfortunately I am struggling at the point where I am not able to add users….. Central Administration > Application Management > Policy for Web Application > Add Users the user is not found when I check names, yes I use MyProvider:username BTW I tested my custom provider in ASP.NET 2.0 and it works super Please advice, thanks a lot”

First of all , you don’t need to specify MyProvider:username for the user to be resolved. In a healthy FBA configuration putting username alone should be okay. However , when you hover over the username , it must show MyProvider:username as the tooltip. This is one of the ways that you can make sure your user belongs to your custom authentication provider , not a similar name in AD or any other authentication providers.

Secondly, the fact that your custom authentication provider works in your ASP.NET application or even in your SharePoint web application (whatever zone you specify for your FBA stuff ) doesn’t guarantee that it should work in Central administration or SSP. They are different web applications running under different application pools (they better be ;)) with different web.config files. Here are two things that I’d check:

1) FBA settings ( Are they the same in SharePoint Web Application and Central admin) – You don’t need to include role provider settings in central admin.

2) Debug your authentication provider to see if it gets initialized. Then step through your various methods and see if there is an exception thrown somewhere in GetUser method that is not propagated up in the call stack or you have handled it without logging it (bad bad idea). Make sure you do an IISRESET and browse to your site once before you attach to the worker process, because your auth provider might have been already initialized and you don’t see the initialized method get hit again – GetUser gets hit everytime a name is resolved. This is where a good instrumented code comes to light to nail down the issue. Please Please make sure that you write plenty of tracing code in every piece of functionality you develop. I guarantee that you won’t regret it. Debugging can be done in two ways. One way is what I described above or you can place a trace on the SQL server side to see if the communication with back-end database ever happens and what gets transmitted. Many people would prefer the latter one.

Hope this helps,

Categories: MOSS 2007 Tags:

The Importance of Network Load Balancing (Part 1): Introduction

February 21st, 2008 1 comment

Hello guys! I’m back to my little cyber space and ready to write about SharePoint stuff again – This time about one of my favorite topics , load balancing. What else can I write about? Seriously! How to improve your relationship with your wife!? 😉 This time I ‘d like to talk about the importance of network load balancing in your development cycle and how it can cause some confusions and potentially unexpected results when you deploy your work to a farm installation. All of us have had prior experiences in which everything works perfectly on our machine, but the minute we deploy our stuff to a customer’s site and we go live , things gets messy. Quite frankly, the mentality that your code is not gonna live on a single machine forever really helps you to look at the bigger picture while you are developing or you’re drawing at the architect’s table. For example……Hey dude! your code is going to be executed in a multi-threaded environment so you got to be careful when caching objects  or , God forbid , caching objects that are not thread safe. Here is another one: Man! iterating through all these list items are not really a good idea, why don’t find a faster approach to retrieve what you want? .Yeah , you keep hearing these advices , some people listen up, improve their coding techniques and some others don’t give a damn. You?…….. okay, okay , I heard ya loooud and clear.I’m happy to hear that you belong to the first group, so let’s relax and slouch down while I’m cooking another SharePoint recipe for you (titled “The Importance of Network Load Balancing”) in my new blog posts series.

Here are the action items that I have in mind at the moment and planning to write about in the upcoming posts:

Part 1) Introduction: This is what you’re right in it!! I don’t really know what to say here. Sounds like Metadata about metadata.

Part 2) Setup: How to setup a load-balanced environment for our testing purposes using Microsoft’s Network Load Balancing technology that is included with Windows 2003 Server operating system. I know NLB doesn’t scale and is not what many organizations utilize to distribute client demands between multiple servers , but that’s not important for our testing purposes (You can always use your intelligence to overcome shortcomings, right?) . No matter what you use for load balancing , these posts would be an interesting tidbit of information that maybe valuable to you.

Part 3) Features , Event handlers and Timer jobs: This is pretty much self-explanatory. In this post ,I would like to talk about the things that you should consider while developing features, event handlers and timers jobs deployed to web farms.

Part 4) Participating in a shared forms authentication : Thankfully, You can have forms authentication in a distributed environment in a farm (or even across multiple applications on a single WFE server). When forms authentication is enabled across multiple applications, users are not challenged to enter their credentials again when switching between those apps. In this post, I will show you how forms based authentication cookie can be shared across multiple web applications even in a load-balanced environment. Yes, you may not need an SSO solution at all and you don’t even know it!

Honestly , it is quite scary when you (as a blogger) announce something like this to your readers whilst you haven’t even written one word of your posts – It is just bunch of thoughts in your mind and experiences . Well, I might come back to this page and move things around a bit, but writing an agenda like this really helps me in two ways:

1) To create a path to follow.

2) To be obliged to come back and get the job done. For me this is even more important than the first one, because a lot of times I have an idea and I think wow! this can turn out to be a great blog post , but I slack and that post never get written. I know I know I’m lazy :)

Categories: MOSS 2007 Tags:

Anonymous Users In SharePoint (Part 3) : Welcome Guest

February 10th, 2008 20 comments

In previous posts, I mentioned that I would show you how to extend Solution 1 and 2 by somehow merging them into an existing authentication provider and finally packaging everything into a feature called “Guest Account Enabler”. An obvious benefit to creating a feature is that it makes it optional to have the guest account functionality in your site. In this post and for the sake of brevity, I assume that Internet Zone (Protected by FBA) is the zone that you want to have the Guest Account enabled , but nothing prevents you from changing the code below to extend it to other zones using the same authentication provider as Internet zone.

If you haven’t already read Part1 and Part 2 , here are the links:

The first step is to create our ‘Global.asax’ file. While our ‘Global.asax’ is very similar to the ones shown in Solution 1 and 2 in part 2, it has one important difference. It constructs the ‘Guest’ account only if the relevant feature is activated. Here is the code for new ‘Global.asax’.

  1. <%@ Assembly Name="Microsoft.SharePoint"%>
  2. <%@ Application Language="C#" Inherits="Microsoft.SharePoint.ApplicationRuntime.SPHttpApplication" %>
  3. <script  RunAt='server'>
  4. public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)
  5. {
  6.         //Extract the forms authentication cookie
  7.         string cookieName = FormsAuthentication.FormsCookieName;
  8.         HttpCookie authCookie = Context.Request.Cookies[cookieName];
  9.         if (null == authCookie)
  10.         {
  11.             // There is no authentication cookie. Check to see if Guest Account feature is activated
  12.             // If Feature is Activated, ValidateUser would return Guest Account Context
  13.             if (Membership.ValidateUser("Guest", ""))
  14.                 {
  15.                     FormsAuthentication.SetAuthCookie("Guest", true);
  16.                 }
  17.         }        
  18. }
  19. </script>

Only one thing needs to be highlighted here: Membership.ValidateUser(“Guest”, “”). I added this condition to ‘Global.asax’ to check to see if the feature is activated or not. As you will see later in this post , ValidateUser() method of our custom authentication provider will return False if the corresponding feature is not activate. As such “Guest Account” security context (FBA token) won’t be constructed for anonymous users in FormsAuthentication_OnAuthenticate and annonymous users will continue to their journey in your site as an unnamed identity as before.

With the ‘Global.asax’ properly coded , the next step is create the a Feature. All Features must contain a Feature definition file, so go ahead and add a new XML file to your project named Feature.xml and add the following code to the file.

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <Feature xmlns="http://schemas.microsoft.com/sharepoint/"
  3.    Description="Enables a guest account that can be targetted for annonymous users"
  4.    Id="602D09E4-4F0D-4058-951D-55059BA87943"
  5.    Scope="Site"
  6.    Hidden="False"
  7.    Title="Guest account enabler for annonymous users"
  8.    Version="1.0.0.0"
  9.    ReceiverAssembly="CustomAuthProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=88ce7fc8bcece008"
  10.    ReceiverClass="CustomAuthProvider.GuestFeatureReceiver">
  11.   <ElementManifests>
  12.     <ElementFile Location="global.asax" />
  13.   </ElementManifests>
  14. </Feature>

Well, our feature declares a FeatureReciever that needs to be coded to take care of some deployment tasks for us. This includes copying our custom ‘Global.asax’ file (declared as ElementFile in ElementManifests node) to the root directory of site that servers incoming traffic from internet (Internet Zone). Here is the FeatureActivated method:

  1. public override void FeatureActivated(SPFeatureReceiverProperties properties)
  2.         {
  3.             SPSite site = properties.Feature.Parent as SPSite;    // get site reference
  4.             SPWebApplication webApp = site.WebApplication as SPWebApplication;
  5.  
  6.             foreach (SPUrlZone zone in webApp.IisSettings.Keys)
  7.             {
  8.                 if (zone == SPUrlZone.Internet)
  9.                 {
  10.                     // The settings of the IIS application to copy global.asax file to.    
  11.                     SPIisSettings oSettings = webApp.IisSettings[zone];
  12.                     // Determine the source and destination path    
  13.                     string sourcePath = string.Format(@"{0}\FEATURES\{1}\", SPUtility.GetGenericSetupPath("Template"),@"\GuestEnablerFeature" );
  14.                     string destPath = oSettings.Path.ToString();
  15.                     File.Copy(Path.Combine(sourcePath, "global.asax"), Path.Combine(destPath, "global.asax"), true);
  16.  
  17.                 }
  18.             }
  19.         }

With the shell of our Feature created , the next step is going to the actual Authentication Provider code and change following overridden methods to support “Guest Account” capability for anonymous users.

  • ValidateUser
  • GetUser
  • GetAllUsers
  • FindUsersByName
  1. public override bool ValidateUser(string username, string password)
  2. {
  3.  //Check to see if Guest Enabler Feature is installed and activated
  4.  bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, "602D09E4-4F0D-4058-951D-55059BA87943");
  5.  if (username == "Guest" &amp;&amp; showGuestAccount) return true;
  6.  //the rest of your code to validate non-guest users goes here
  7. }
  1. public override MembershipUser GetUser(string username, bool userIsOnline)
  2. {
  3.   //Check to see if Guest Enabler Feature is installed and activated
  4.   bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, "602D09E4-4F0D-4058-951D-55059BA87943");
  5.   return Utils.GetUserByFilter(this.Name,this.ConnectionString,Utils.UsersFilter.UserName,username,showGuestAccount);
  6. }
  1. public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
  2. {
  3.   //Check to see if Guest Enabler Feature is installed and activated
  4.   bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, "602D09E4-4F0D-4058-951D-55059BA87943");
  5.   return Utils.GetUsersByFilter(this.Name,this.ConnectionString,Utils.UsersFilter.Empty,null,pageIndex,pageSize,out totalRecords,showGuestAccount);
  6. }
  1. public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
  2. {
  3.   //Check to see if Guest Enabler Feature is installed and activated
  4.   bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, "602D09E4-4F0D-4058-951D-55059BA87943");
  5.   return Utils.GetUsersByFilter(this.Name,this.ConnectionString,Utils.UsersFilter.UserName,usernameToMatch,pageIndex,pageSize,out totalRecords,  showGuestAccount);
  6. }

Three things need some explanation in the preceding code snippets.

  1. I have used couple helper methods in the above code snippets which is included below.
  2. You need to virtually construct the ‘Guest Account’ as a MembershipUser object in GetUser() when this method is called.
  3. You need to add the ‘Guest Account’ to the MembershipUserCollection in GestAllUsers() and FindUsersByName() so “People Picker” can resolve that account for targeting content or assigning permission purposes.
  1. public class Utils
  2.     {
  3.         public enum UsersFilter { Email, UserName, Empty }
  4.         public static bool IsSiteFeatureActivated(HttpContext context,string featureID)
  5.         {
  6.             SPWebApplication app = SPWebApplication.Lookup(new Uri(context.Request.Url.AbsoluteUri));
  7.             using(SPSite site = app.Sites[0])
  8.             {
  9.                 SPFeature returnsiteFeatures = site.Features[new Guid(featureID)];
  10.                 return (returnsiteFeatures != null ? true : false);
  11.             }                                
  12.          }
  13.         //Create a MembershipUserCollection consisting of our single user.
  14.         public static MembershipUserCollection AddAnnonUserToMembershipCollection(string procviderName, MembershipUserCollection users)
  15.         {
  16.             users.Add(new MembershipUser(procviderName, "Guest",
  17.             "Guest", string.Empty, string.Empty, string.Empty, true, false,
  18.             DateTime.MinValue, DateTime.MinValue, DateTime.MinValue,
  19.             DateTime.MinValue, DateTime.MinValue));
  20.             return users;
  21.         }
  22.         public static MembershipUserCollection GetUsersByFilter(string procviderName, string connectionString, UsersFilter filter, string value, int pageIndex, int pageSize, out int totalRecords, bool includeGuestUser)
  23.         {
  24.             MembershipUserCollection resultUsers;
  25.  
  26.             // Code to get All users based on the filer specified and populate resultUsers collection goes here (removed for code brevity)
  27.  
  28.             //Check to see if you need to add Guest account to the collection
  29.             if (includeGuestUser)
  30.             {
  31.                 totalRecords += 1;
  32.                 return AddAnnonUserToMembershipCollection(procviderName, resultUsers);
  33.             }
  34.             else
  35.                 return resultUsers;
  36.  
  37.         }
  38.         public static MembershipUser GetUserByFilter(string providerName, string connectionString, UsersFilter filter, string value, bool includeGuestUser)
  39.         {          
  40.             if (value == "Guest" && includeGuestUser)
  41.                 return new MembershipUser(providerName, "Guest", "Guest", string.Empty, string.Empty, string.Empty, true, false, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue);
  42.  
  43.             //rest of your code to get non-guest user goes here
  44.          
  45.         }
  46.     }

Once you have created all of the required pieces and deployed your feature in a solution package (not explained in this post), then you will see that our Feature in the Features collection as shown below:

Go ahead and activate the feature. You will notice that the ‘Global.asax’ of your internet zone is replaced with your own custom one and subsequently any anonymous calls will be executed under the security context of our virtual ‘Guest Account’. If you disable the feature everything goes back to normal life.

Feature is activated
 
Guest Account is resolved in all zones
 
People Picker shows Guest Account
 
Feature is deactivated
 

This pretty much concludes my three-part series on Anonymous Users in SharePoint. Hope you have found them useful.

Categories: MOSS 2007 Tags:

Anonymous Users In SharePoint (Part 2) : Solutions

February 8th, 2008 7 comments

In Part 1 of this series, I gave you a quick tour of how to enable anonymous access and explained some of the issues regarding anonymous access in both Authoring and FBA zones. In this post I am going to walk you through some of the solutions and workarounds and also the drawbacks of each approach. In part 3, I am going to put everything together and come up with a more real life solution.

Solution 1:This solution is probably the easiest one to overcome the limitation that anonymous user is not a real configurable identity in FBA zone. It is all about making SharePoint think that anonymous user has logged in under the security context of whatever you construct in ‘Global.asax’ .Yes, the ‘Global.asax’ file in the FBA protected site must be modified, so that the ‘Application_AuthenticateRequest’ event (To authenticate the caller) knows how to construct a valid cookie and assign it to the current unnamed identity (anonymous user). This will be later used by the SharePoint downstream authorization process to define what level of access control an anonymous user has to various resources across your site. You simply add the Guest account to your identity store and replace ‘Global.asax’ of your FBA site with the one below and BINGO! anonymous users will show up as “Guest”. Now , You can target content at “Guest” and you can assign permission. Easy, huh?

This solution can be found here . Here is the gist of it – implemented in ‘Global.asax’ :

  1. <%@ Assembly Name="Microsoft.SharePoint"%>
  2. <%@ Application Language="C#" Inherits="Microsoft.SharePoint.ApplicationRuntime.SPHttpApplication" %>
  3. <script RunAt='server'>
  4.     protected void Application_AuthenticateRequest(Object sender, EventArgs e)
  5.     {
  6.         string cookieName = FormsAuthentication.FormsCookieName;
  7.         HttpCookie authCookie = Context.Request.Cookies[cookieName];
  8.         if (authCookie == null)
  9.         {
  10.             FormsAuthentication.SetAuthCookie("guest", false);
  11.         }      
  12.     }
  13. </script>

Drawbacks : First of all It only works in FBA zones. Secondly, many people may not want to add that Guest account in their identity store (for whatever reason) so that would be ideal if we could somehow virtually create that Guest Account. This will lead us to the second solution.

Solution 2: This solution is actually an extension to the above solution by leveraging a custom authentication provider behind the scenes and by virtually creating the Guest context (It’s called Annon in that post). As I alluded to earlier , the whole point here is not to have a Guest Account in the underlying identity store instead virtually creating it at runtime. This POC is very well documented so there is no point for me to reinvent the wheel. Go ahead and read up for yourself, but make sure that you come back and read the rest of this article 😉

Drawbacks : Like the first solution it doesn’t solve the issue we saw in Part 1 with regards to NTLM or Kerberos protected sites , it is only for FBA protected sites period . On the other hand, it is a proof of concept (actually a brilliant one) , but in reality you have to merge this POC into your already developed custom authentication provider , right? Otherwise what’s the point of having an authentication provider that only returns one user (Annon user)?! Plus , that would be great if we could package the whole damn thing into a feature that can be turned ON and off that gives you more flexibility. In part 3, you will see how I will combine all these solutions to make a much more kind of production ready functionality.

Solution 3 : Wrap the content in SPSecurityTrimmedControl and then you should be able to target it to different SPBasePermissions or AuthenticationRestrictions. In the example below, the entire web part zone is surrounded with SPSecurityTrimmedControl tag with PermissionsString set to CreateSSCSite (Self-Service Site Creation) . The “CreateSSCSite ” permission is common to the “Members”, “Owners” and “Visitors” group permission levels, but is not set for the “Limited Access” permission level which is assigned to anonymous users permission level. As you can see, thumbs-up picture doesn’t show up for anonymous users.

  1. <sharepoint:spsecuritytrimmedcontrol runat="server" permissionsstring="CreateSSCSite">
  2. <webpartpages:webpartzone runat="server" frametype="TitleBarOnly" id="Left" title="loc:Left">
  3. <zonetemplate>
  4. <webpartpages:imagewebpart runat="server" verticalalignment="Middle" allowedit="True" allowconnect="True" connectionid="00000000-0000-0000-0000-000000000000" title="Site Image" isincluded="True" dir="Default" backgroundcolor="transparent" isvisible="True" alternativetext="Microsoft Windows SharePoint Services Logo" allowminimize="True" exportcontrolledproperties="True" zoneid="Left" id="g_60b6aa26_dd7b_4973_aaa0_ece2f2110920" horizontalalignment="Center" imagelink="/_layouts/images/thumbsup.jpg" exportmode="All" allowhide="True" suppresswebpartchrome="False" chrometype="None" framestate="Normal" missingassembly="Cannot import this Web Part." allowremove="True" helpmode="Modeless" frametype="None" allowzonechange="True" partorder="1" description="Use to display pictures and photos." __markuptype="vsattributemarkup" __webpartid="{60B6AA26-DD7B-4973-AAA0-ECE2F2110920}" webpart="true"></webpartpages:imagewebpart>
  5. </zonetemplate>
  6. </webpartpages:webpartzone>
  7. </sharepoint:spsecuritytrimmedcontrol>

Default zone (Authenticated User)

Default Zone (Annonymous User)

   
   

FBA zone (Authenticated User)

FBA Zone (Annonymous User)

   

Drawbacks : The main drawback of this approach is that it is mainly useful for content targeting and anonymous users still cannot fully interact with document libraries as we discussed in Part 1. CreateSSCSite is also not granted at “Quick Deploy Users” which means people in that group will also treated like anonymous users. If you can live with that limitation then this solution can be an option for you.

Categories: MOSS 2007 Tags: