When you write custom code in Sharepoint webparts, your code will run with your credentials.
Not everybody has Full Control, so when a user has only read rights, the code will throw an access denied error when the code needs access to objects that are not in the scope of the user credentials.... (example: add the username in the ReadBy properties of an item).
What you need is impersonation, run your code with the help of a user who has just enough rights to run it. Sharepoint has a built-in function to accomplish this: SPSecurity.RunWithElevatedPrivileges, it runs with the System Account User.
Some things you should know when using SPSecurity.RunWithElevatedPrivileges:
- in the delegate function, you must build a new SPSite/SPWeb object (like SPSite siteColl = new SPSite(App.SITE_COLLECTION_URL)) and you can't use the SPContext.Current.Web, because the SPContext runs with the current context (with current user).
- Also set the AllowUnsafeUpdates property of the site/web where you will be updating/accessing stuff to true, to be sure you don't get an error like "The security validation for this page is invalid" (see more below). If you don't do it, you code will work, but when returning from the delegate function, the error will arise..
Code sample:
I run the "Elevated code" when the button btn is clicked in a webpart:
void btn_Click(object sender, EventArgs e)
{
{
SPSecurity.RunWithElevatedPrivileges(TestSec);
}
catch (Exception ex)
{
throw ex;
}
}
public void TestSec()
{
SPSite siteColl = new SPSite(App.SITE_COLLECTION_URL);
SPWeb site = siteColl.AllWebs[App.WEB_NAME];
SPList list = site.Lists["Test"];
SPListItem testItem = list.GetItemById(1);
site.AllowUnsafeUpdates = true;
SPRoleDefinition roleDefinitionContributor = site.RoleDefinitions.GetByType(SPRoleType.Contributor);
SPRoleAssignment roleAssignment = new SPRoleAssignment("DOMAIN\\USERNAME", "", "", "");
roleAssignment.RoleDefinitionBindings.Add(roleDefinitionContributor);
////Check for permission inheritance, and break if necessary
if (!testItem.HasUniqueRoleAssignments)
{
testItem.BreakRoleInheritance(false); //pass true to copy role assignments from parent, false to start from scratch
}
testItem.RoleAssignments.Add(roleAssignment);
testItem.Update();
site.AllowUnsafeUpdates = false;
siteColl.Close();
site.Close();
}
Error when not setting AllowUnsafeUpdates = true:
Server Error in '/' Application.
--------------------------------------------------------------------------------
The security validation for this page is invalid. Click Back in your Web browser, refresh the page, and try your operation again.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Runtime.InteropServices.COMException: The security validation for this page is invalid. Click Back in your Web browser, refresh the page, and try your operation again.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[COMException (0x8102006d): The security validation for this page is invalid. Click Back in your Web browser, refresh the page, and try your operation again.]
Microsoft.SharePoint.Library.SPRequestInternalClass.UpdateRoleAssignment(String bstrUrl, Guid& pguidScopeId, Int32 lPrincipalID, Object& pvarArrIdRolesToAdd, Object& pvarArrIdRolesToRemove) +0
Microsoft.SharePoint.Library.SPRequest.UpdateRoleAssignment(String bstrUrl, Guid& pguidScopeId, Int32 lPrincipalID, Object& pvarArrIdRolesToAdd, Object& pvarArrIdRolesToRemove) +119
[SPException: The security validation for this page is invalid. Click Back in your Web browser, refresh the page, and try your operation again.]
OfficialMail.WebParts.TestWebPart.btn_Click(Object sender, EventArgs e) +94
System.Web.UI.WebControls.Button.OnClick(EventArgs e) +105
System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +107
System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +7
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +11
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +33
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +5102
Sources: