Question

I have a custom ribbon icon "Enlist" deployed to each document library. A normal (i.e. non-admin) user can click this icon; when he does, I would like to permanently save the document library information (ID, name) somewhere. A central admin can then (via a custom central admin page) see a list of all document libraries enlisted by users.

I'm thinking about using SPPersistedObject to store the document library information when a user clicks on the ribbon icon. However, afaik this requires farm admin privileges, which means that normal users will encounter an error when clicking "Enlist". Would running the code that writes SPPersistedObject with elevated privileges resolve this problem, or could an error still occur? This is what I mean:

// Execute this code when a ribbon icon is clicked by a normal user
SPSecurity.RunWithElevatedPrivileges(delegate()
{
    // Write DocumentLibrary Id and Name to some custom SPPersistedObject
});

MSDN says that RunWithElevatedPrivileges() "runs under the Application Pool identity, which has site collection administrator privileges on all site collections hosted by that application pool." Is this enough to update SPPersistedObject?

Should I override SPPersistedObject.HasAdditionalUpdateAccess to always return true, or would this still not be enough for least-privileged environments? If not, what options do I have to store such information other than a custom database?

Was it helpful?

Solution

Elevated privileges is not enough to write to the configuration database. With elevated privileges you will use the the web application app pool’s identitty, which has only read permission to the config db by default. Of course admin user (which runs the central admin should have read and write permissions).

See more at: http://joelblogs.co.uk/2010/10/10/persisting-configuration-data-in-sharepoint-2010-with-sppersistedobject/

SPPersistedObject.HasAdditionalUpdateAccess

still needs the non farm admin user to be impersonated to farm admin user.

One final topic that requires some coverage is security. As described earlier, the HOS exists in the farm configuration database. Strictly speaking it exists to support administrative functionality however it has uses beyond this scope. For example, if we’re building a custom timer job and we want to have the ability to submit tasks to our job for completion. It’s most likely that these tasks will originate within a non-administrative scope. Possibly as the result of a user clicking a button on a webpart on some other site collection. With previous versions of SharePoint, this simply wasn’t possible. Only farm administrators could update the HOS. However with SharePoint 2010, an addition has been made to the base SPPersistedObject that opens up the HOS to other users. To see how this works, we can modify our Main function as follows:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace PersistedObjectDemo
{
    internal class Program
    {
        private const int LOGON_TYPE_INTERACTIVE = 2;
        private const int LOGON_TYPE_PROVIDER_DEFAULT = 0;

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern bool LogonUser(string userName, string domain, string password, int logonType,
                                            int logonProvider, ref IntPtr accessToken);

        private static void Main(string[] args)
        {
            try
            {
                IntPtr accessToken = IntPtr.Zero;
                if (LogonUser("non-farm-admin-username", "your-domain-name", "your-password", LOGON_TYPE_INTERACTIVE,
                              LOGON_TYPE_PROVIDER_DEFAULT, ref accessToken))
                {
                    using (var identity = new WindowsIdentity(accessToken))
                    {
                        using (identity.Impersonate())
                        {
                            MyPersistedObject obj = MyPersistedObject.Local;
                   //delete existing object and recreate to keep things simple
                            obj.Delete();
                            obj = MyPersistedObject.Local;
                            obj.Foo = "Some updated value";
                            obj.Update();
                        }
                    }
                }

            }
            catch (Exception ex)
            {
                Console.Write(ex);
            }

            Console.WriteLine("Press return to exit");
            Console.ReadLine();
        }
    }
}

This code simulates an attempt by a non-farm admin user to update the HOS. If we run this we’ll see the following exception which is our gotcha number 3:

System.Security.SecurityException: Access denied. at Microsoft.SharePoint.Administration.SPPersistedObject.BaseUpdate() at PersistedObjectDemo.MyPersistedObject.Update()

Unsurprisingly, trying to update the HOS using a non-farm admin user throws an exception. However, if we add the following code to MyPersistedObject.cs and run our program again, we can see that the HOS is updated appropriately.

protected override bool HasAdditionalUpdateAccess()
{
    return true;
}

Of course, in a real-world application, we’d probably want to add some security checking in this function rather than simply returning true and allowing anybody to update our object but hopefully this illustrates the point sufficiently.

There is one other gotcha with regard to security – when SharePoint is 2010 installed, by default only the farm admin account has read/write access to the config database. In a least-privilege installation, all web applications other then Central Administration, will be running under their own service account and these accounts will not have write access to the config database. Consequently, adding, deleting and updating objects in the HOS will not be possible. Although overriding HasAdditionalUpdateAccess allows us a bit more control over who can update the HOS, if the underlying app pool account doesn’t have the appropriate database permissions we’re going to come unstuck pretty quickly.

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top