{"id":523,"date":"2008-02-10T17:42:03","date_gmt":"2008-02-10T22:42:03","guid":{"rendered":"http:\/\/blogs.devhorizon.com\/reza\/?p=523"},"modified":"2008-02-10T18:27:00","modified_gmt":"2008-02-10T23:27:00","slug":"anonymous-users-in-sharepoint-part-3-welcome-guest","status":"publish","type":"post","link":"https:\/\/blogs.devhorizon.com\/reza\/2008\/02\/10\/anonymous-users-in-sharepoint-part-3-welcome-guest\/","title":{"rendered":"Anonymous Users In SharePoint (Part 3) : Welcome Guest"},"content":{"rendered":"<p>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 &#8220;Guest Account Enabler&#8221;. 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.<\/p>\n<p>If you haven&#8217;t already read Part1 and Part 2 , here are the links:<\/p>\n<ul>\n<li><a href=\"https:\/\/blogs.devhorizon.com\/reza\/?p=498\">Anonymous Users In SharePoint (Part 1) : Introduction<\/a><\/li>\n<li><a href=\"https:\/\/blogs.devhorizon.com\/reza\/?p=508\">Anonymous Users In SharePoint (Part 2): Solutions<\/a><\/li>\n<\/ul>\n<p>The first step is to create our &#8216;Global.asax&#8217; file. While our &#8216;Global.asax&#8217; is very similar to the ones shown in Solution 1 and 2 in part 2, it has one important difference. It constructs the &#8216;Guest&#8217; account only if the relevant feature is activated. Here is the code for new &#8216;Global.asax&#8217;.<\/p>\n<p>[csharp]<br \/>\n<%@ Assembly Name=\"Microsoft.SharePoint\"%><br \/>\n<%@ Application Language=\"C#\" Inherits=\"Microsoft.SharePoint.ApplicationRuntime.SPHttpApplication\" %><br \/>\n<script  RunAt='server'>\npublic void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)\n{\n        \/\/Extract the forms authentication cookie\n        string cookieName = FormsAuthentication.FormsCookieName;\n        HttpCookie authCookie = Context.Request.Cookies[cookieName];\n        if (null == authCookie)\n        {\n            \/\/ There is no authentication cookie. Check to see if Guest Account feature is activated \n            \/\/ If Feature is Activated, ValidateUser would return Guest Account Context \n            if (Membership.ValidateUser(\"Guest\", \"\"))\n                {\n                    FormsAuthentication.SetAuthCookie(\"Guest\", true);\n                }\n        }        \n}\n<\/script><br \/>\n[\/csharp]<\/p>\n<p>Only one thing needs to be highlighted here: Membership.ValidateUser(&#8220;Guest&#8221;, &#8220;&#8221;). I added this condition to &#8216;Global.asax&#8217; 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  &#8220;Guest Account&#8221;  security context (FBA token) won&#8217;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.<\/p>\n<p>With the &#8216;Global.asax&#8217; 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.<\/p>\n<p>[xml]<br \/>\n<?xml version=\"1.0\" encoding=\"utf-8\" ?><br \/>\n<Feature xmlns=\"http:\/\/schemas.microsoft.com\/sharepoint\/\"\n    Description=\"Enables a guest account that can be targetted for annonymous users\"\n    Id=\"602D09E4-4F0D-4058-951D-55059BA87943\"\n    Scope=\"Site\"\n    Hidden=\"False\"\n    Title=\"Guest account enabler for annonymous users\"\n    Version=\"1.0.0.0\"\n    ReceiverAssembly=\"CustomAuthProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=88ce7fc8bcece008\"\n    ReceiverClass=\"CustomAuthProvider.GuestFeatureReceiver\"><br \/>\n  <ElementManifests><br \/>\n    <ElementFile Location=\"global.asax\" \/><br \/>\n  <\/ElementManifests><br \/>\n<\/Feature><br \/>\n[\/xml]<\/p>\n<p>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 &#8216;Global.asax&#8217; 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:<\/p>\n<p>[csharp]<br \/>\n    public override void FeatureActivated(SPFeatureReceiverProperties properties)<br \/>\n        {<br \/>\n            SPSite site = properties.Feature.Parent as SPSite;    \/\/ get site reference<br \/>\n            SPWebApplication webApp = site.WebApplication as SPWebApplication;<\/p>\n<p>            foreach (SPUrlZone zone in webApp.IisSettings.Keys)<br \/>\n            {<br \/>\n                if (zone == SPUrlZone.Internet)<br \/>\n                {<br \/>\n                    \/\/ The settings of the IIS application to copy global.asax file to.<br \/>\n                    SPIisSettings oSettings = webApp.IisSettings[zone];<br \/>\n                    \/\/ Determine the source and destination path<br \/>\n                    string sourcePath = string.Format(@&#8221;{0}\\FEATURES\\{1}\\&#8221;, SPUtility.GetGenericSetupPath(&#8220;Template&#8221;),@&#8221;\\GuestEnablerFeature&#8221; );<br \/>\n                    string destPath = oSettings.Path.ToString();<br \/>\n                    File.Copy(Path.Combine(sourcePath, &#8220;global.asax&#8221;), Path.Combine(destPath, &#8220;global.asax&#8221;), true);<\/p>\n<p>                }<br \/>\n            }<br \/>\n        }<br \/>\n[\/csharp]<\/p>\n<p>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 &#8220;Guest Account&#8221; capability for anonymous users.<\/p>\n<ul>\n<li>ValidateUser<\/li>\n<li>GetUser<\/li>\n<li>GetAllUsers<\/li>\n<li>FindUsersByName<\/li>\n<\/ul>\n<p>[csharp]<br \/>\npublic override bool ValidateUser(string username, string password)<br \/>\n{<br \/>\n \/\/Check to see if Guest Enabler Feature is installed and activated<br \/>\n bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, &#8220;602D09E4-4F0D-4058-951D-55059BA87943&#8221;);<br \/>\n if (username == &#8220;Guest&#8221; &amp;&amp; showGuestAccount) return true;<br \/>\n \/\/the rest of your code to validate non-guest users goes here<br \/>\n}<br \/>\n[\/csharp]<\/p>\n<p>[csharp]<br \/>\npublic override MembershipUser GetUser(string username, bool userIsOnline)<br \/>\n{<br \/>\n  \/\/Check to see if Guest Enabler Feature is installed and activated<br \/>\n  bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, &#8220;602D09E4-4F0D-4058-951D-55059BA87943&#8221;);<br \/>\n  return Utils.GetUserByFilter(this.Name,this.ConnectionString,Utils.UsersFilter.UserName,username,showGuestAccount);<br \/>\n}<\/p>\n<p>[\/csharp]<\/p>\n<p>[csharp]<br \/>\npublic override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)<br \/>\n{<br \/>\n  \/\/Check to see if Guest Enabler Feature is installed and activated<br \/>\n  bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, &#8220;602D09E4-4F0D-4058-951D-55059BA87943&#8221;);<br \/>\n  return Utils.GetUsersByFilter(this.Name,this.ConnectionString,Utils.UsersFilter.Empty,null,pageIndex,pageSize,out totalRecords,showGuestAccount);<br \/>\n}<\/p>\n<p>[\/csharp]<\/p>\n<p>[csharp]<br \/>\npublic override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)<br \/>\n{<br \/>\n  \/\/Check to see if Guest Enabler Feature is installed and activated<br \/>\n  bool showGuestAccount = Utils.IsSiteFeatureActivated(HttpContext.Current, &#8220;602D09E4-4F0D-4058-951D-55059BA87943&#8221;);<br \/>\n  return Utils.GetUsersByFilter(this.Name,this.ConnectionString,Utils.UsersFilter.UserName,usernameToMatch,pageIndex,pageSize,out totalRecords,  showGuestAccount);<br \/>\n}<br \/>\n[\/csharp]<\/p>\n<p>Three things need some explanation in the preceding code snippets.<\/p>\n<ol>\n<li>I have used couple helper methods in the above code snippets which is included below.<\/li>\n<li>You need to virtually construct the &#8216;Guest Account&#8217; as a MembershipUser object in GetUser()  when this method is called.<\/li>\n<li>You need to add the &#8216;Guest Account&#8217; to the MembershipUserCollection in GestAllUsers() and FindUsersByName() so &#8220;People Picker&#8221; can resolve that account for targeting content or assigning permission purposes.<\/li>\n<\/ol>\n<p>[csharp]<br \/>\n  public class Utils<br \/>\n    {<br \/>\n        public enum UsersFilter { Email, UserName, Empty }<br \/>\n        public static bool IsSiteFeatureActivated(HttpContext context,string featureID)<br \/>\n        {<br \/>\n            SPWebApplication app = SPWebApplication.Lookup(new Uri(context.Request.Url.AbsoluteUri));<br \/>\n            using(SPSite site = app.Sites[0])<br \/>\n            {<br \/>\n                SPFeature returnsiteFeatures = site.Features[new Guid(featureID)];<br \/>\n                return (returnsiteFeatures != null ? true : false);<br \/>\n            }<br \/>\n         }<br \/>\n        \/\/Create a MembershipUserCollection consisting of our single user.<br \/>\n        public static MembershipUserCollection AddAnnonUserToMembershipCollection(string procviderName, MembershipUserCollection users)<br \/>\n        {<br \/>\n            users.Add(new MembershipUser(procviderName, &#8220;Guest&#8221;,<br \/>\n            &#8220;Guest&#8221;, string.Empty, string.Empty, string.Empty, true, false,<br \/>\n            DateTime.MinValue, DateTime.MinValue, DateTime.MinValue,<br \/>\n            DateTime.MinValue, DateTime.MinValue));<br \/>\n            return users;<br \/>\n        }<br \/>\n        public static MembershipUserCollection GetUsersByFilter(string procviderName, string connectionString, UsersFilter filter, string value, int pageIndex, int pageSize, out int totalRecords, bool includeGuestUser)<br \/>\n        {<br \/>\n            MembershipUserCollection resultUsers;<\/p>\n<p>            \/\/ Code to get All users based on the filer specified and populate resultUsers collection goes here (removed for code brevity)<\/p>\n<p>            \/\/Check to see if you need to add Guest account to the collection<br \/>\n            if (includeGuestUser)<br \/>\n            {<br \/>\n                totalRecords += 1;<br \/>\n                return AddAnnonUserToMembershipCollection(procviderName, resultUsers);<br \/>\n            }<br \/>\n            else<br \/>\n                return resultUsers;<\/p>\n<p>        }<br \/>\n        public static MembershipUser GetUserByFilter(string providerName, string connectionString, UsersFilter filter, string value, bool includeGuestUser)<br \/>\n        {<br \/>\n            if (value == &#8220;Guest&#8221; &#038;&#038; includeGuestUser)<br \/>\n                return new MembershipUser(providerName, &#8220;Guest&#8221;, &#8220;Guest&#8221;, string.Empty, string.Empty, string.Empty, true, false, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue);<\/p>\n<p>            \/\/rest of your code to get non-guest user goes here<\/p>\n<p>        }<br \/>\n    }<br \/>\n[\/csharp]<\/p>\n<p>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:<\/p>\n<table style=\"border-collapse: collapse\" border=\"0\">\n<tr>\n<td style=\"border: 0.5pt solid black; padding-left: 7px; padding-right: 7px\"><img decoding=\"async\" src=\"https:\/\/blogs.devhorizon.com\/reza\/wp-content\/uploads\/2008\/02\/021008-2242-anonymousus1.png\" \/><\/td>\n<\/tr>\n<\/table>\n<p>\nGo ahead and activate the feature. You will notice that the &#8216;Global.asax&#8217; 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 &#8216;Guest Account&#8217;. If you disable the feature  everything goes back to normal life.<\/p>\n<table style=\"border-collapse: collapse\" border=\"0\">\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\">Feature is activated<\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\"><img decoding=\"async\" src=\"https:\/\/blogs.devhorizon.com\/reza\/wp-content\/uploads\/2008\/02\/021008-2242-anonymousus2.png\" \/><\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\">&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\">Guest Account is resolved in all zones<\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\"><img decoding=\"async\" src=\"https:\/\/blogs.devhorizon.com\/reza\/wp-content\/uploads\/2008\/02\/021008-2242-anonymousus3.png\" \/><\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\">&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\">People Picker shows Guest Account<\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\"><img decoding=\"async\" src=\"https:\/\/blogs.devhorizon.com\/reza\/wp-content\/uploads\/2008\/02\/021008-2242-anonymousus4.png\" \/><\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\">&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\">Feature is deactivated<\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\"><img decoding=\"async\" src=\"https:\/\/blogs.devhorizon.com\/reza\/wp-content\/uploads\/2008\/02\/021008-2242-anonymousus5.png\" \/><\/td>\n<\/tr>\n<tr>\n<td style=\"padding-left: 7px; padding-right: 7px\">&nbsp;<\/td>\n<\/tr>\n<\/table>\n<p>This pretty much concludes my three-part series on Anonymous Users in SharePoint. Hope you have found them useful.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 &#8220;Guest Account Enabler&#8221;. An obvious benefit to creating a feature is that it makes it optional to have the guest account [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[43],"tags":[],"class_list":["post-523","post","type-post","status-publish","format-standard","hentry","category-moss-2007"],"_links":{"self":[{"href":"https:\/\/blogs.devhorizon.com\/reza\/wp-json\/wp\/v2\/posts\/523","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.devhorizon.com\/reza\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.devhorizon.com\/reza\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.devhorizon.com\/reza\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.devhorizon.com\/reza\/wp-json\/wp\/v2\/comments?post=523"}],"version-history":[{"count":0,"href":"https:\/\/blogs.devhorizon.com\/reza\/wp-json\/wp\/v2\/posts\/523\/revisions"}],"wp:attachment":[{"href":"https:\/\/blogs.devhorizon.com\/reza\/wp-json\/wp\/v2\/media?parent=523"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.devhorizon.com\/reza\/wp-json\/wp\/v2\/categories?post=523"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.devhorizon.com\/reza\/wp-json\/wp\/v2\/tags?post=523"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}