Search code examples
sharepoint-2010

Best practices for Run with elevated privelege


Among the two approaches which one is preferred with run with elevated privileges?

First Approach:

SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    using (SPSite curSite = new SPSite(SPContext.Current.Site.ID))
                    {
                        using (SPWeb web = curSite.OpenWeb(SPContext.Current.Web.ID))
                        {
                            try
                            {
                                web.AllowUnsafeUpdates = true;
                                \\ do your stuff
                            }
                            catch (Exception e)
                            {

                            }
                            finally
                            {
                                web.AllowUnsafeUpdates = false;
                                web.Dispose();
                            }
                        }
                    }
                });

Second Approach:

SPSite oSite = SPContext.Current.Site;
SPWeb oWeb = SPContext.Current.Web;
SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    using (SPSite curSite = new SPSite(oSite.ID))
                    {
                        using (SPWeb web = curSite.OpenWeb(oWeb.ID))
                        {
                            try
                            {
                                web.AllowUnsafeUpdates = true;
                                \\ do your stuff
                            }
                            catch (Exception e)
                            {

                            }
                            finally
                            {
                                web.AllowUnsafeUpdates = false;
                                web.Dispose();
                                oWeb.Dispose();
                                oSite.Dispose();
                            }
                        }
                    }
                });

Are any of them suspected to throw "The web being updated was changed by an external process" exception ?


Solution

  • First of all, you should make a call to SPWeb.ValidateFormDigest() or SPUtility.ValidateFormDigest() before elevating the code. This will get rid of unsafe update not allowed with GET request, and avoid you to setup the AllowUnsafeUpdate property.

    Secondly, as Nigel Whatling mentioned, you are disposing context object in your second code. You don't have to dispose them. To be simple, only dispose object you are instantiating yourself. A code like yours can cause side effects, as other SharePoint component may require access to SPContext.Current.XX objects. This is probably the root of your issue.

    Thirdly, as you are using the using construct, you don't have to call .Dispose() on the variable you set up in the using header. Actually, the role (and actually benefit) of the using construct is that you don't have to care to dispose the object. As soon as the block code exits, even if there was an exception, the .Dispose() method is called on your object.

    To conclude, your code should be changed to that :

    SPUtility.ValidateFormDigest();
    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        using (SPSite curSite = new SPSite(SPContext.Current.Site.ID))
        {
            using (SPWeb web = curSite.OpenWeb(SPContext.Current.Web.ID))
            {
                  // Do stuff here
            }
        }
    });
    

    A side note: to elevate a code, you have two options. The one you use here (call to SPSecurity.RunWithElevatedPrivileges) or instantiating a new SPSite with the SystemAccount token :

    using (SPSite curSite = new SPSite(
        SPContext.Current.Site.ID, 
        SPContext.Current.Site.SystemAccount.UserToken
    ))
    {
        using (SPWeb web = curSite.OpenWeb(SPContext.Current.Web.ID))
        {
              // Do stuff here
        }
    }
    

    This will allow you to run elevated code outside a webapplication.

    You should also consider using some utility code to wrap such operations in a more functional way. I'm used to use code like this :

    public static void RunWithElevatedPrivileges(this SPWeb web, Action<SPSite, SPWeb> codeToRunElevated)
    {
        if (CheckIfElevated(web))
        {
            codeToRunElevated(web.Site, web);
        }
        else
        {
            using (var elevatedSite = new SPSite(web.Site.ID, web.AllUsers["SHAREPOINT\\system"].UserToken))
            {
                using (var elevatedWeb = elevatedSite.OpenWeb(web.ID))
                {
                    codeToRunElevated(elevatedSite, elevatedWeb);
                }
            }
        }
    }
    
    /// <summary>
    /// Indicates whether the context has been elevated
    /// </summary>
    public static bool CheckIfElevated(SPWeb web)
    {
        return web.CurrentUser.LoginName == "SHAREPOINT\\system";
    }
    

    Using such code, you can simply do, somewhere in your code :

    SPContext.Current.Web.RunWithElevatedPrivileges((elevatedSite, elevatedWeb)=>{
         // do something will all privileges
    });