Home > MOSS 2007 > Anonymous Users In SharePoint (Part 3) : Welcome Guest

Anonymous Users In SharePoint (Part 3) : Welcome Guest

February 10th, 2008 Leave a comment Go to 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:
  1. March 19th, 2008 at 08:36 | #1

    I’m happy to see that you have extended my simple global.asax idea into a solution! The FBA:IEE people I’ve been working with would love this addition. Can you either send me the solution package? I’d love to post this on my CodePlex site and let the FBA:IEE guys know.
    -niskypto (http://www.codeplex.com/wssguestaccount)

  2. Reza Alirezaei
    March 27th, 2008 at 13:52 | #2

    John , I sent an email to the email address you provided when posting this comment. I will try to post the solution as a separate download and you can take it from there.Give me some time 🙂

  3. Reza Alirezaei
    March 30th, 2008 at 18:41 | #3

    Source code is now available here : https://blogs.devhorizon.com/reza/?p=551

  4. Anil
    July 1st, 2008 at 02:46 | #4

    This can be used with windows authentication also? Because i tried this using with windows athentication not form based.

    Please advise me if i am doing some thing wrong here.

  5. admin
    July 4th, 2008 at 17:38 | #5

    No , this is an FBA solution.

  6. Thomas Goddard
    August 8th, 2008 at 16:53 | #6

    Can you provide more detail on what you had to do after configuring the guest account login with FBA in order to get workflows to start? What kind of permissions do you give the guest user? Are you able to run SharePoint designer created Workflows as the guest user? More insight on this front would be very nice since I am running into issues with workflows and permissions when the user adds a new item to a list.

  7. Reza Alirezaei
    August 12th, 2008 at 23:53 | #7

    Sorry Thomas , but I haven’t tested this with Workflow. I will do this the first chance I get.

  8. Andrew
    September 26th, 2008 at 14:13 | #8

    We are trying to use this solution to allow users to allow public access to an unsecured portion of our site, while maintaining a secured portion for employees. We have the FBA portion of the site set up as the public access, and a windows based authentication set up for employees. We have deployed the feature, and everything worked for us, except that we need to adjust the permission of the guest account. How do we go about doing this? The guest user is of course the only user that is allowed access to the FBA site, and the guest user account does not (by design) have access to make changes to the Permissions and Users. If I try access the Permissions and Users screen through the Windows based authentication site the guest user account does not show up. Our Windows Based Authentication uses Active Directory. How do we get the guest user to show up in order to adjust the permissions? Is there something wrong in our thinking about this issue?

  9. Reza Alirezaei
    September 26th, 2008 at 14:41 | #9

    Andrew, No there is absolutely nothing wrong in your thinking. What you need to do is to introduce your FBA membership settings to your Windows Zone *too*. This way , your Windows zone can see your FBA users (in your case , your guest user only) and basically can assign permission to it. Have a good weekend!

  10. Andrew
    September 26th, 2008 at 14:45 | #10

    Thank you so much for the quick reply. Could you expand just a litte on what you mean by “introduce your FBA membership settings to your Windows Zone *too*?

  11. Burt
    January 6th, 2009 at 12:23 | #11

    Is there an issue changing the Global.asax to use Codebehind with the Global.asax.cs file? It says Parser Error “Could not load type ‘Global'” Line 2.
    Here is the basic changes I made.
    Global.asax:

    Global.asax.cs:
    public class Global : Microsoft.SharePoint.ApplicationRuntime.SPHttpApplication {}

  12. Burt
    January 6th, 2009 at 12:27 | #12

    Here is the encoded missing code from the question above
    Golbal.asax:
    <%@ Assembly Name="Microsoft.SharePoint %>
    <%@ Application Language="C#" AutoEventWireup="true" Codebehind="Global.asax.cs" Inherits="Global" %>

  13. Burt
    January 12th, 2009 at 12:00 | #13

    A team member figured out how to get the CodeBehind to work. It required the 4 part Inherits name.

    Original Global.asax:
    <%@ Application CodeBehind="Global.cs" Inherits="Global" Language="C#" %>

    Working Global.asax:
    <%@ Application CodeBehind="Global.cs" Inherits="MyNamespace.Global, MyNamespace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5a142a0129a6cf4f" Language="C#" %>

  14. Jim
    May 18th, 2009 at 14:12 | #14

    I am a relative newbie to sharepoint but know .NET code fairly well. I tried to implement this solution, first compiling then running the deploy batch file… made the necessary web.config changes but when trying to “activate” the feature, it does not show up nor does it appear to work. What simple mistake am I making when either trying to deploy or activate? Other?

  15. Reza Alirezaei
    May 18th, 2009 at 14:22 | #15

    @Jim

    You should go to site settings -> site collection features and it should be there.

  16. nh
    May 20th, 2009 at 12:56 | #16

    I am facing the same problem as Thomas.
    Can you provide more detail on what has to bo done after configuring the guest account login with FBA in order to get workflows to start?
    Thanks

  17. Thomas Goddard
    July 31st, 2009 at 16:19 | #17

    Hi nh,

    I ended up allowing contribute permissions on the list for the guest user, which is the only way I was able to get it to work. The next thing I did was hide the library that they’re creating an item in so that they wouldn’t be able to access it any other way. It’s a total hack but I am not sure what else to try and this was after lots of trial and error.

    I am now trying to get rid of this pesky issue where if a user accesses the form as the first page they hit, the cookie has not yet been set and they are redirected to the login form. It seems wonky to have to set the guest cookie then response.redirect the user to the page they were trying to hit but it might just be the only way. BLAH!

  18. Chris
    August 17th, 2010 at 10:18 | #18

    Just take a look.It is helpful for my SP2010 site.Thank you.

  19. razieh
    August 2nd, 2011 at 02:00 | #19

    hi
    how to enable add item in document library share point 2010 for anonymous user?

  20. thomas
    November 15th, 2011 at 17:18 | #20

    Is it possible to configure My Sites to utilize guest accounts? For example, a manufacturing plant has 500 users that don’t have AD accounts. Is it remotely possible for them to connect to a My Site for the entire manufacturing plant?

You must be logged in to post a comment.