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.