In a
Page Visitor Tracking web part I have been writing, I use the SPSecurity.RunWithElevatedPrivileges
function to add or update a line in a Visitor Tracking list on the current
site. The idea being that not all visitors to the page will have the rights to
write to this list, so some level of impersonation is needed.
Frustratingly, even though I had used this function to elevate the
privileges of the current user, I was still getting an "Access Denied" message
when running the functionality under my test user.
e.g. (Note, this code sample has been cut down a great deal to illustrate
the point - no "try...catch" blocks, for example. It should be used only as a guide, and
not copied to be used as is)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
protected override
void CreateChildControls()
{
//Get the current web
SPWeb web = SPControl.GetContextWeb(Context);
//Get the list
to record visits
visList =
web.Lists[_visitorList];
//See if this user already exists in list
_existItemId =
GetVisitorItemId(visList);
//If not in list, add them
if (_existItemId == 0)
{
//Run the AddCurrentUserToList function with elevated
priv's
SPSecurity.CodeToRunElevated
elevatedAddCurrentUserToList = new
SPSecurity.CodeToRunElevated(AddCurrentUserToList);
SPSecurity.RunWithElevatedPrivileges(elevatedAddCurrentUserToList);
}
else
{
//Run the AddCurrentUserToList function with elevated
priv's
SPSecurity.CodeToRunElevated
elevatedIncrementNumberOfVisits = new
SPSecurity.CodeToRunElevated(IncrementNumberOfVisits);
SPSecurity.RunWithElevatedPrivileges(elevatedIncrementNumberOfVisits);
}
}
private
void AddCurrentUserToList()
{
//Create new
ListItem
SPListItemCollection items =
visList.Items;
SPListItem newItem =
items.Add();
//Set field
values for new ListItem
//Update
Item
newItem.Update();
}
private
void IncrementNumberOfVisits()
{
//Get the ListItem to update
//Increment the
number of visits
//Update the item
updateItem.Update();
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Because of this, I changed my code so that I made a new reference to the
SPWeb in the elevated code block, and obtained a new reference to the
list:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
protected override void
CreateChildControls()
{
//Get the current web
SPWeb
web = SPControl.GetContextWeb(Context);
_curWeb = web.Url;
SPList visList = null;
//Get the list to record visits
visList = web.Lists[_visitorList];
//See if this user already exists in
list
_existItemId =
GetVisitorItemId(visList);
//If not in list, add them
if (_existItemId == 0)
{
//Run the AddCurrentUserToList function with elevated
priv's
SPSecurity.CodeToRunElevated
elevatedAddCurrentUserToList = new
SPSecurity.CodeToRunElevated(AddCurrentUserToList);
SPSecurity.RunWithElevatedPrivileges(elevatedAddCurrentUserToList);
}
else
{
//Run the AddCurrentUserToList function with elevated
priv's
SPSecurity.CodeToRunElevated
elevatedIncrementNumberOfVisits = new
SPSecurity.CodeToRunElevated(IncrementNumberOfVisits);
SPSecurity.RunWithElevatedPrivileges(elevatedIncrementNumberOfVisits);
}
}
private
void AddCurrentUserToList()
{
//Get list
SPSite site = new
SPSite(_curWeb);
SPWeb web =
site.OpenWeb();
SPList visList =
web.Lists[_visitorList];
web.AllowUnsafeUpdates = true;
//Create new ListItem
SPListItemCollection items = visList.Items;
SPListItem newItem = items.Add();
//Set field
values for new ListItem
//Update
Item
newItem.Update();
web.AllowUnsafeUpdates = false;
//Cleanup
web.Dispose();
site.Dispose();
}
private
void IncrementNumberOfVisits()
{
//Get list
SPSite site = new SPSite(_curWeb);
SPWeb web =
site.OpenWeb();
SPList visList =
web.Lists[_visitorList];
web.AllowUnsafeUpdates = true;
//Get the
ListItem to update
//Increment the number of visits
//Update the item
updateItem.Update();
web.AllowUnsafeUpdates = false;
//Cleanup
web.Dispose();
site.Dispose();
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SPList's, SPWeb's etc get the identity of the user context they are created
under associated with them for their entire lifetime. Like most things, seems
obvious once you understand it.
To get around this, I no longer use the SPList globally, but instead keep
the URL of the web globally, and use this in the elevated code blocks to derive
an SPSite, SPWeb and finally the required SPList.
Important to note that this as the SPWeb that I create in the elevated code
block is a different SPWeb to what I am using outside this block (it has the
SHAREPOINT\Security identity associated with it, not the current users) it can
(and should) safely disposed of from within this block.
-----------------------------------------------------------
UPDATE: As can be seen in the comments below, Keith Dahlby has posted a much more elegant way of achieving the elevation of privileges by using the site's token.
I have changed my code to follow this (and incorporated the
functionality in my SharePoint.Common library) and recommend reading
his article - see http://solutionizing.net/2009/01/06/elegant-spsite-elevation/
Keep up the good work Keith!