Tag Archive for 'security'

SharePoint FBA: Basic “All Authenticated Users” Role Provider

When managing users and groups within a SharePoint Web application configured to use Windows Integrated Authentication,  there is a convenient “Add all authenticated users” link that adds a special Active Directory group – NT AUTHORITY\authenticated users – to the Users/Groups People Editor.  This group refers to any non-anonymous user, which if you ask me, seems like a pretty common group to have around.  However, when working within a SharePoint Web application configured to use Forms Based Authentication (FBA), this convenient group is no longer available.

When using FBA, the only “non-SharePoint” groups available to us are the roles exposed by an ASP.Net Role Provider.  If you are already using a custom Role Provider and are not able to make changes to it, then you can stop here.  This post is not for you.  If you are like me though, and are using FBA merely for authentication and are leveraging SharePoint for all authorization, then the single “All Authenticated Users” role is all I need from my Role Provider.  As a result, there is no need to use a heavy weight Role Provider (i.e., the SQL Role Provider) to accomplish this, but rather roll your own very dumb role provider.  There is only a single method that you will need to implement – GetRolesForUser – in which you can assume the user is already authenticated and always return the “All Authenticated Users” role for the user. Here is the Role Provider I am currently using:

using System;
using System.Web.Security;
 
namespace Trentacular.Web.Security
{
    public class SimpleAllAuthenticatedUsersRoleProvider : RoleProvider
    {
        public const string AllAuthenticatedUsersRoleName = "All Authenticated Users";
 
        public override string ApplicationName { get; set; }
 
        public override string[] GetRolesForUser(string username)
        {
            return new[] { AllAuthenticatedUsersRoleName };
        }
 
        #region Methods Not Implemented
 
        public override string[] GetAllRoles() { throw new NotImplementedException(); }
        public override bool IsUserInRole(string username, string roleName) { throw new NotImplementedException(); }
        public override bool RoleExists(string roleName) { throw new NotImplementedException(); }
        public override void AddUsersToRoles(string[] usernames, string[] roleNames) { throw new NotImplementedException(); }
        public override void CreateRole(string roleName) { throw new NotImplementedException(); }
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) { throw new NotImplementedException(); }
        public override string[] FindUsersInRole(string roleName, string usernameToMatch) { throw new NotImplementedException(); }
        public override string[] GetUsersInRole(string roleName) { throw new NotImplementedException(); }
        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) { throw new NotImplementedException(); }
 
        #endregion
    }
}

After rolling your own role provider, you will need to register it in the web.config inside the <system.web> section as such:

<roleManager enabled="true" defaultProvider="SimpleAllAuthenticatedUsersRoleProvider">
    <providers>
        <add name="SimpleAllAuthenticatedUsersRoleProvider" type="Trentacular.Web.Security.SimpleAllAuthenticatedUsersRoleProvider, Trentacular.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=aaaaaaaaaaaaaaaa" />
    </providers>
</roleManager>

SharePoint Security Validation and Unsafe Updates

Seeing this? The security validation for this page is invalid. I am sure this error has good intentions, but it is showing its face far too often. Hristo Pavlov has written a good explanation on AllowUnsafeUpdates here.

If you read my previous post and are attempting to update list items as the System User, you will likely run into this problem.  Setting AllowUnsafeUpdates to true for the duration of the update has allowed me to get around this exception.  Here is the helper method I am using to accomplish this:

        public static void DoUnsafeUpdate(SPWeb web, Action action)
        {
            bool allowUnsafeUpdates = web.AllowUnsafeUpdates;
            web.AllowUnsafeUpdates = true;
            action();
            web.AllowUnsafeUpdates = allowUnsafeUpdates;
        }

Couple this with the helper methods in my previous post, and you now can do both together:

        public static void DoUnsafeUpdateAsSystemUser(SPWeb web, SPWebAction action)
        {
            DoAsSystemUser(web, delegate(SPWeb systemWeb)
            {
                DoUnsafeUpdate(systemWeb, delegate()
                {
                    action(systemWeb);
                });
            });
        }

SharePoint Security “Do As System User”

Scenario

You are writing a custom web part or control and want to be able to access a list or resource within your code that the current user otherwise does not have permissions to.  For me, this happens quite frequently.  For example:

  • Reading info from a list dedicated to storing Application Settings that only an administrator can read and edit
  • Modifying permissions on a list item after creating it
  • Modifying a list item other than the specific item in context
  • Updating Web properties
  • Kicking off a workflow from code as a result of a users action (event handlers are also a good place to do this, which already run under the context of the System user)

What Didn’t Work

SPSecurity.RunWithElevatedPriveleges – this only impersonates the user account running the thread in order to access network resources, etc.   But if you are accessing SharePoint resources using the SPContext.Current.Web, your SPWeb object is still limited to the permission set of the original user initiating the request.

What Did Work

Create a new site using the Site.SystemAccount UserToken and then open the web using this Site.  Here are two helper methods I am using for doing just this:

        public delegate void SPWebAction(SPWeb web);
 
        public static void DoAsSystemUser(SPWeb web, SPWebAction action)
        {
            SPUser systemUser = web.Site.SystemAccount;
            DoAsUser(web, systemUser, action);
        }
 
        public static void DoAsUser(SPWeb web, SPUser user, SPWebAction action)
        {
            // If we are already running as the given User Token, just pass the web along
            if (web.CurrentUser != null &amp;&amp;
                web.CurrentUser.UserToken.CompareUser(user.UserToken))
            {
                action(web);
                return;
            }
 
            using (SPSite site = new SPSite(web.Site.ID, user.UserToken))
            {
                using (SPWeb userWeb = site.OpenWeb(web.ID))
                {
                    action(userWeb);
                }
            }
        }

Two more things that still need to be addressed are Security Validation and Unsafe Updates, which I will talk about in the next post.