Archive

Archive for March, 2011

SharePoint: How to get SystemAccount token

March 25th, 2011 No comments

     As you probably know RunWithElevatedPrivileges allows to run code under the Application Pool identity, which has site collection administrator privileges on all site collections hosted by that application pool. In my opinion RunWithElevatedPrivileges provides too many rights for code, which usually operates in the bounds of only one certain site collection. That is why instead of using RunWithElevatedPrivileges I prefer dealing with an elevated SPSite-object created with SPUserToken of SystemAccount of the target site collection. SystemAccount or SHAREPOINT\system is an alias for the site collection administrators and it has full control over all content in the site collection. We can get it using property SystemAccount.UserToken of SPSite-object. However sometimes the current user doesn’t have permissions to access that property, in this case we will have to use RunWithElevatedPrivileges I’ve criticized above :). The method retrieving the token of SystemAccount can look like the following:

protected static SPUserToken GetSystemToken(SPSite spSite)
{
    SPUserToken res = null;
    bool oldCatchAccessDeniedException = spSite.CatchAccessDeniedException;
    try
    {
        spSite.CatchAccessDeniedException = false;
        res = spSite.SystemAccount.UserToken;
    }
    catch (UnauthorizedAccessException)
    {
        SPSecurity.RunWithElevatedPrivileges(delegate()
        {
            using (SPSite elevatedSPSite = new SPSite(spSite.ID))
                res = elevatedSPSite.SystemAccount.UserToken;      // (***)
        });
    }
    finally
    {
        spSite.CatchAccessDeniedException = oldCatchAccessDeniedException;
    }
    return res;
}

     Here spSite.CatchAccessDeniedException=false allows us to catch and handle UnauthorizedAccessException by ourself, and if the exception happens, we elevates privileges to read the user token by means of RunWithElevatedPrivileges. As we know the most of SharePoint objects somehow refers to SPSite and SPWeb objects, in context of which these SP-objects were created. From this viewpoint the GetSystemToken can look potentially buggy in the line marked with (***), because method returns SPUserToken while its parent elevatedSPSite is being disposed. To justify this code we should take a look at SPUserToken class kindly provided by .Net Reflector:

public sealed class SPUserToken
{
    // Fields
    private byte[] m_token;

    // Methods
    public SPUserToken(byte[] token);
    public bool CompareUser(SPUserToken userTokenCheck);

    // Properties
    public byte[] BinaryToken { get; }
}

     As you can see SPUserToken is just an array of bytes, it doesn’t contain any references to any parental SharePoint objects, therefore we can return it from the method freely. Once SPUserToken is received, it can be cached for a while and reused.

     If you don’t have any original SPSite-object (for example, you have a simple console application) you should create it, e.g.

protected static SPUserToken GetSystemToken(string siteUrl)
{
    SPUserToken res = null;
    using (SPSite spSite = new SPSite(siteUrl))
        res = GetSystemToken(spSite);
    return res;
}

Thanks!

Related posts:

SharePoint: How to Restore a SharePoint Site from a Database Backup

March 22nd, 2011 No comments

     From time to time I restore the Web-application I’m working on from the SQL-backup of database of production server. The main reason for that is to have the actual local environment and to make sure that a new functionality I’ve implemented will work correctly on production server with actual data. Our production server and developer machines are in one domain and for this case the usual sequence of actions is:

  1. Restore backup into a new database using Microsoft SQL Server Management Studio;
  2. Create a new Web-application (Central Administration->Application Management->Create or extend Web application->Create a new Web application):
    • scroll to “Database Name and Authentication”-section and replace Database Name (generated by default) with the name of just created database;
    • scroll to “Application Pool”-section and make sure that a security account for a new application pool has the required permissions on the content database you’ve recovered;
  3. Go to restored Site Collection Administrators (Central Administration->Application Management->Site collection administrators) of just created Web-application and check that they are valid in the new environment, if not, replace them with valid ones;
  4. [Optional] Here some people suggest doing stsadm –o upgrade –inplace –url http://<just created web application url>, but I don’t, because everything works fine in my case;

Happy restoring!

Related posts:

SharePoint: ResetRoleInheritance and BreakRoleInheritance wrappers

March 21st, 2011 No comments

     A while back I wrote about a number of methods lead to reinitializing of SPWeb-object and, as the result, to resetting AllowUnsafeUpdates to false (SharePoint: Updates are currently disallowed on GET requests). Methods ResetRoleInheritance and BreakRoleInheritance of SPListItem class are the examples of such behavior. I use them very often, that is why I decided to develop a couple of wrappers that allow to restore AllowUnsafeUpdates to true automatically after original methods have finished their work.

public static void SafeResetRoleInheritance(SPListItem item)
{
    bool oldAllowUnsafeUpdate = item.Web.AllowUnsafeUpdates;
    item.ResetRoleInheritance();
    if(item.Web.AllowUnsafeUpdates != oldAllowUnsafeUpdate)
        item.Web.AllowUnsafeUpdates = oldAllowUnsafeUpdate;
}
public static void SafeBreakRoleInheritance(SPListItem item, bool copyRoleAssignments)
{
    bool oldAllowUnsafeUpdate = item.Web.AllowUnsafeUpdates;
    item.BreakRoleInheritance(copyRoleAssignments);
    if (item.Web.AllowUnsafeUpdates != oldAllowUnsafeUpdate)
        item.Web.AllowUnsafeUpdates = oldAllowUnsafeUpdate;
}

     Each wrapper preserves the current value of SPWeb.AllowUnsafeUpdates, invokes the original method, then tests whether the AllowUnsafeUpdates is changed and if so, it restores the old value back.

Related posts:

SharePoint: Updates are currently disallowed on GET requests

March 4th, 2011 No comments

     When I need to set unique permissions to a SPListItem I usually use the code like the following:

using (SPSite spSite = new SPSite("some url"))
{
    using (SPWeb spWeb = spSite.OpenWeb())
    {
        bool oldAllowUnsafeUpdates = spWeb.AllowUnsafeUpdates;
        spWeb.AllowUnsafeUpdates = true;
        spWeb.Update();

        try
        {
            SPList spList = spWeb.Lists["some list"];

            SPListItem spLisItem = spList.GetItemById(someId);
            spLisItem.BreakRoleInheritance(false);

            SPRoleDefinition reader = spWeb.RoleDefinitions.GetByType(SPRoleType.Reader);
            SPGroup someGrp = spWeb.Groups["some group"];

            SPRoleAssignment roleAssignment = new SPRoleAssignment(someGrp);
            roleAssignment.RoleDefinitionBindings.Add(reader);
            spListItem.RoleAssignments.Add(roleAssignment); // (***) exception

        }
        catch (Exception ex)
        {
            // logging
        }

        spWeb.AllowUnsafeUpdates = oldAllowUnsafeUpdates;
    }
}

     It works fine almost everywhere: in feature receivers, in jobs, in console applications and so on. But today I’ve found out that it doesn’t work correctly if it runs from aspx-page’s code-behind when we have GET request (Page.IsPostBack = false). In the line marked (***) I receives a traditional exception – “Updates are currently disallowed on GET requests. To allow updates on a GET, set the ‘AllowUnsafeUpdates’ property on SPWeb”. As you can see I set spWeb.AllowUnsafeUpdates to true and even do spWeb.Update() (though it’s unnecessary in most cases), but nothing helps. Wrapping this code in SPSecurity.RunWithElevatedPrivileges doesn’t help either.

     After debugging for a while I’ve noticed that spWeb.AllowUnsafeUpdates gets false after spLisItem.BreakRoleInheritance:

// here spWeb.AllowUnsafeUpdates = true
spLisItem.BreakRoleInheritance(false);
// here spWeb.AllowUnsafeUpdates = false

     It should be noted that the same happens when we have a POST request (Page.IsPostback = true), but in this case it doesn’t cause exception. Interestingly that we have some kind of special treatment for GET request here 🙂

     The reason of such behavior has been found, as usual, by means of .NET Reflector. Not going into details I say that the calling of BreakRoleInheritance leads to the calling of Invalidate() method of SPWeb. Let’s take a look at this method:

internal void Invalidate()
{
    this.ReleasePinnedResource();
    if (this.m_Request != null)
    {
        if (this.m_RequestOwnedByThisWeb)
        {
            SPRequestManager.Release(this.m_Request);
        }
        this.m_Request = null;
    }
    this.m_bInited = false;
    this.m_bPublicPropertiesInited = false;
    this.m_Url = null;
}

     It looks like Invalidate releases some resources and cleans itself (m_bInited = false), in the same time, doesn’t dispose itself, but just provokes reinitializing during the next address to it. After reinitialization spWeb.AllowUnsafeUpdates turns into false. An evident workaround is to set true to spWeb.AllowUnsafeUpdates again after BreakRoleInheritance.

// some code is skipped
spLisItem.BreakRoleInheritance(false);
spWeb.AllowUnsafeUpdates = true;
spWeb.Update();
// some code is skipped

     I think there are many methods, which can cause SPWeb.Invalidate, for example, ResetRoleInheritance does the same, and, in a few special cases, SPWeb.Update either. That is why be ready to add restoring of AllowUnsafeUpdates to true. I hope this post will save time for somebody.

Related posts: