Anonymous Users In SharePoint (Part 3) : Welcome Guest
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:
- Anonymous Users In SharePoint (Part 1) : Introduction
- Anonymous Users In SharePoint (Part 2): Solutions
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’.
- <%@ Assembly Name="Microsoft.SharePoint"%>
- <%@ Application Language="C#" Inherits="Microsoft.SharePoint.ApplicationRuntime.SPHttpApplication" %>
- <script RunAt='server'>
- public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)
- {
- //Extract the forms authentication cookie
- string cookieName = FormsAuthentication.FormsCookieName;
- HttpCookie authCookie = Context.Request.Cookies[cookieName];
- if (null == authCookie)
- {
- // There is no authentication cookie. Check to see if Guest Account feature is activated
- // If Feature is Activated, ValidateUser would return Guest Account Context
- if (Membership.ValidateUser("Guest", ""))
- {
- FormsAuthentication.SetAuthCookie("Guest", true);
- }
- }
- }
- </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.
- <?xml version="1.0" encoding="utf-8" ?>
- <Feature xmlns="http://schemas.microsoft.com/sharepoint/"
- Description="Enables a guest account that can be targetted for annonymous users"
- Id="602D09E4-4F0D-4058-951D-55059BA87943"
- Scope="Site"
- Hidden="False"
- Title="Guest account enabler for annonymous users"
- Version="1.0.0.0"
- ReceiverAssembly="CustomAuthProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=88ce7fc8bcece008"
- ReceiverClass="CustomAuthProvider.GuestFeatureReceiver">
- <ElementManifests>
- <ElementFile Location="global.asax" />
- </ElementManifests>
- </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:
- public override void FeatureActivated(SPFeatureReceiverProperties properties)
- {
- SPSite site = properties.Feature.Parent as SPSite; // get site reference
- SPWebApplication webApp = site.WebApplication as SPWebApplication;
- foreach (SPUrlZone zone in webApp.IisSettings.Keys)
- {
- if (zone == SPUrlZone.Internet)
- {
- // The settings of the IIS application to copy global.asax file to.
- SPIisSettings oSettings = webApp.IisSettings[zone];
- // Determine the source and destination path
- string sourcePath = string.Format(@"{0}\FEATURES\{1}\", SPUtility.GetGenericSetupPath("Template"),@"\GuestEnablerFeature" );
- string destPath = oSettings.Path.ToString();
- File.Copy(Path.Combine(sourcePath, "global.asax"), Path.Combine(destPath, "global.asax"), true);
- }
- }
- }
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
- public override bool ValidateUser(string username, string password)
- {
- //Check to see if Guest Enabler Feature is installed and activated
- bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, "602D09E4-4F0D-4058-951D-55059BA87943");
- if (username == "Guest" && showGuestAccount) return true;
- //the rest of your code to validate non-guest users goes here
- }
- public override MembershipUser GetUser(string username, bool userIsOnline)
- {
- //Check to see if Guest Enabler Feature is installed and activated
- bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, "602D09E4-4F0D-4058-951D-55059BA87943");
- return Utils.GetUserByFilter(this.Name,this.ConnectionString,Utils.UsersFilter.UserName,username,showGuestAccount);
- }
- public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
- {
- //Check to see if Guest Enabler Feature is installed and activated
- bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, "602D09E4-4F0D-4058-951D-55059BA87943");
- return Utils.GetUsersByFilter(this.Name,this.ConnectionString,Utils.UsersFilter.Empty,null,pageIndex,pageSize,out totalRecords,showGuestAccount);
- }
- public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
- {
- //Check to see if Guest Enabler Feature is installed and activated
- bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, "602D09E4-4F0D-4058-951D-55059BA87943");
- return Utils.GetUsersByFilter(this.Name,this.ConnectionString,Utils.UsersFilter.UserName,usernameToMatch,pageIndex,pageSize,out totalRecords, showGuestAccount);
- }
Three things need some explanation in the preceding code snippets.
- I have used couple helper methods in the above code snippets which is included below.
- You need to virtually construct the ‘Guest Account’ as a MembershipUser object in GetUser() when this method is called.
- 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.
- public class Utils
- {
- public enum UsersFilter { Email, UserName, Empty }
- public static bool IsSiteFeatureActivated(HttpContext context,string featureID)
- {
- SPWebApplication app = SPWebApplication.Lookup(new Uri(context.Request.Url.AbsoluteUri));
- using(SPSite site = app.Sites[0])
- {
- SPFeature returnsiteFeatures = site.Features[new Guid(featureID)];
- return (returnsiteFeatures != null ? true : false);
- }
- }
- //Create a MembershipUserCollection consisting of our single user.
- public static MembershipUserCollection AddAnnonUserToMembershipCollection(string procviderName, MembershipUserCollection users)
- {
- users.Add(new MembershipUser(procviderName, "Guest",
- "Guest", string.Empty, string.Empty, string.Empty, true, false,
- DateTime.MinValue, DateTime.MinValue, DateTime.MinValue,
- DateTime.MinValue, DateTime.MinValue));
- return users;
- }
- public static MembershipUserCollection GetUsersByFilter(string procviderName, string connectionString, UsersFilter filter, string value, int pageIndex, int pageSize, out int totalRecords, bool includeGuestUser)
- {
- MembershipUserCollection resultUsers;
- // Code to get All users based on the filer specified and populate resultUsers collection goes here (removed for code brevity)
- //Check to see if you need to add Guest account to the collection
- if (includeGuestUser)
- {
- totalRecords += 1;
- return AddAnnonUserToMembershipCollection(procviderName, resultUsers);
- }
- else
- return resultUsers;
- }
- public static MembershipUser GetUserByFilter(string providerName, string connectionString, UsersFilter filter, string value, bool includeGuestUser)
- {
- if (value == "Guest" && includeGuestUser)
- return new MembershipUser(providerName, "Guest", "Guest", string.Empty, string.Empty, string.Empty, true, false, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue);
- //rest of your code to get non-guest user goes here
- }
- }
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.
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)
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 🙂
Source code is now available here : https://blogs.devhorizon.com/reza/?p=551
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.
No , this is an FBA solution.
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.
Sorry Thomas , but I haven’t tested this with Workflow. I will do this the first chance I get.
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?
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!
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*?
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 {}
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" %>
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#" %>
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?
@Jim
You should go to site settings -> site collection features and it should be there.
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
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!
Just take a look.It is helpful for my SP2010 site.Thank you.
hi
how to enable add item in document library share point 2010 for anonymous user?
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?