Impersonation in SharePoint In Details
Impersonation is the process of executing code in the context (or on behalf) of another user's credentials. You may want to use existing SPUser credentials or you may want to assume the Application Pool Credentials. Also, sometimes you might want to use impersonation to make use of existing Windows user accounts and their permissions.
Before you decide for a specific approach for impersonation, it's important that you understand the difference between the SharePoint Security Context and the Windows Security Context. In a SharePoint Visual webparts or application pages, apart from accessing the protected resources inside SharePoint (Lists, Libraries, files and so on), you may need to access the external resources (Shared Folder, SQL Server and so on) as well. Since the access to these external resources is not controlled by SharePoint, a correct impersonation approach should be adopted to make successful calls to external systems.
Also, You should keep in mind not to use Impersonation or Elevation of privilege to bypass the security, always use it to work with the security model your site needs.
SharePoint Sites are configured to run under the account of the requesting user. If you look at the web.config file for any SharePoint web application, you will find the following entry.
- <identity impersonate="true" />
With this setting, every request is instructed to run under the Windows security context of the current user. It means that when your custom webpart or page tries to access an external resource (such as a file system , database , or Web service) it runs under the Windows account of the requesting user.
[Note: In a plain ASP.NET website, by default, impersonation is disabled. So, all the code is executed using w3wp process account, which is the Application Pool Account configured in IIS.]
In case of Classic Mode Authentication (Windows), the user identity is the same in both the Windows and SharePoint security context. However,If you are using Claims Based Authentication, Windows security context may take on the identity of the IUSR_MACHINENAME account, or the account is specified in the IIS. This can deny access when accessing external resources because the IUSR_MACHINENAME account typically will not have rights to those resources. In such cases, you may need to use the Secure Store Service (SSS).
Now, Let's explore the ways to impersonate in SharePoint and see when to apply each technique effectively.
1. Using SPSecurity.RunWithElevatedPrivileges
In SharePoint, it's a very popular practice to run code with RunWithElevatedPrivileges. This is commonly used to do an action on behalf of a user with insufficient rights.
The following is an example.
- SPSecurity.RunWithElevatedPrivileges(delegate()
- {
- using (SPSite Site = new SPSite(SPContext.Current.Site))
- {
- using (SPWeb Webb = Site.OpenWeb(SPContext.Current.Web.Url))
- {
- // Perform administrative actions by using the elevated site and web objects.
- // Web.CurrentUser.LoginName gives SHAREPOINTsystem
- // WindowsIdentity.GetCurrent().Name gives Application pool Windows account(ContsoAdmin1)
- // Hence, Both SharePoint Security context and Windows Security context are changed.
- }
- }
- });
Considering the Windows Security Context, the code inside SPSecurity.RunWithElevatedPrivileges block runs under the Application Pool Account of your web application. This is the account under which the worker process (w3wp) runs. Besides running the web application, this Application Pool Account is used as the Windows account that connects to the SharePoint Content and Configuration databases. So the code is authenticated by Application pool acount when it attempts to access external resources, such as the local file system or a SQL Server database.
Considering the SharePoint Security Context, the code inside the SPSecurity.RunWithElevatedPrivileges block runs under the SHAREPOINTSYSTEM account . SHAREPOINTSYSTEM is an identity to which SharePoint maps internally. Any updates you do via SharePoint object model (SPListItem,SPFile) reflects the modified or created by SHAREPOINTSYSTEM.
SHAREPOINTSYSTEM is not recognized by the Windows security subsystem. As said earlier, this account is mapped to the Application Pool Account.
It is important to understand a few important aspects when using RunWithElevatedPrivileges:
- Elevation of privilege occurs only if new SPSite created inside the block
You need to create new SPSite and SPWeb objects inside the SPSecurity.RunWithElevatedPrivileges block using either URL or GUID. If you don't do so or try to use SPSiteSPWeb from SPContext.Current, you will get an Access Denied error on using SPSecurity.RunWithElevatedPrivileges even. Also, never forget to dispose of your objects.
For example, the following code does not elevate the privilege and is the wrong way to use SPSecurity.RunWithElevatedPrivileges:
- SPSecurity.RunWithElevatedPrivileges(delegate()
- {
- SPSite Site = SPContext.Current.Site;
- using (SPWeb Web = elevatedSite.OpenWeb())
- {
- // Performing administrative actions here will give Access Denied exception.
- }
- }
- });
- Improper usage may cause Security Issues
SharePoint objects (SPList, SPFile and so on) that are created or accessed using the elevated SPSite (created inside RunWithElevatedPrivileges block) and SPWeb retain the permissions they were created with. Hence, you should not return these objects outside the RunWithElevatedPrivileges block otherwise it can lead to security issues.
Here is the explanation for this behavior:- SPList taskList=null;
- SPSecurity.RunWithElevatedPrivileges(delegate()
- {
- SPSite Site = SPContext.Current.Site;
- using (SPWeb Web =
- Site.OpenWeb())
- {
- Splist Testlist = Web.Lists.TryGetList["Customer"]
- }
- }
- });
- //This code will succeed even outside the block as it is accessed via elevated SPWeb. Hence Security Risk.
- Testlist.Delete();
When you create an SPSite object, it is persisted by an underlying SPRequest object. The SPRequest remembers which user created the SPSite object. The SPRequest is shared by all objects that are accessed via that SPSite object.
When an SPSite object is created in a RunWithElevatedPrivileges block, the SPRequest object records that the current user is the System Account. For example, If a SPList object is accessed via elevated SPSite object (like SPSite.RootWeb.Lists["Tasks"] ) , it shares the same SPRequest object and is actually an “elevated” object. If this SPList object is passed outside of the RunWithElevatedPrivileges block, it retains its underlying SPRequest object and continues to be elevated. So now if your code further uses this SPList object, you may have a security leak.
- RunWithElevatedPrivileges changes Windows Security Context as well
For example, if you change an Application Pool Account of a web application from ContsoAdmin1 to ContsoAdmin2 , the code running using the SPSecurity.RunWithElevatedPrivileges block still acts and is audited as the SHAREPOINTSYSTEM account inside SharePoint (SharePoint Security Context).
Reflects the changed windows user when a call is made outside SharePoint (Windows Security Context).
So inside the RunWithElevatedPrivileges block, any call to external systems like DB or WebServices will be made by the Windows account of the application pool. It succeeds or does not depend on the permissions that the Windows account has on that external system.
[Note: If the external system is on another server, calls will not succeed even if the Application Pool Account has permissions. This is due to a double-hop issue in NTLM authentication. You need to configure KERBEROS authentication for that or use Secure Store Service.]
- RunWithElevatedPrivileges does not work when HTTPContext is null
RunWithElevatedPrivileges won't work when HTTPContext (SPContext to be more specific) is null. So, you will not have an elevation of privilege when using RunWithElevatedPrivilege in a Console Application, WorkFlow , Timer Job or Event handlers not initiated by a request in a browser.
- RunWithElevatedPrivileges does not work for a Sandbox Solution
RunWithElevatedPrivileges works only if you deploy the component (webpartpage) as a Farm Solution. It does not work for Sandbox Solutions. (For more Sandbox Solution limits, please check here.)
- Some Write Operations may fail even
If the method passed to RunWithElevatedPrivileges includes any write operations, the call to RunWithElevatedPrivileges should be preceded by a call to either SPUtility.ValidateFormDigest() or SPWeb.ValidateFormDigest(). Otherwise, the operation may not succeed.
- To use SPSecurity.RunWithElevatedPrivileges and still retain the CurrentUser's identit
If you run code within a SPSecurity.RunWithElevatedPrivileges block and create new objects, such as list items within a list, the user is automatically assigned as the author or editor as SHAREPOINT\system. However, you may need the user to be the owner of an item with his or her current credentials.
To do this, you must first retrieve the real credentials and then elevate the privileges.
The following example shows how to deal with this issue of two identities.- private void UpdateItem(SPList RestrictedList, bool IsAnonymous)
- {
- SPUser user =SPContext.CurrentWeb.CurrentUser;
- Guid siteID = RestrictedList.ParentWeb.Site.ID;
- Guid webID = RestrictedList.ParentWeb.ID;
- Guid listID = RestrictedList.ID;
- SPSecurity.RunWithElevatedPrivileges(() =>
- {
- using (SPSite site = new SPSite(siteID))
- {
- using (SPWeb web = site.OpenWeb(webID))
- {
- web.AllowUnsafeUpdates = true;
- SPList elevatedList = web.Lists[listID];
- SPListItem item = elevatedList.Items.Add();
- SPUser systemUser = web.AllUsers[@"SHAREPOINT\system"];
- SPFieldUserValue currentUser = new SPFieldUserValue( item.ParentList.ParentWeb, user.ID, user.Name);
- if (!IsAnonymous)
- {
- item["Author"] = currentUser;
- item["Editor"] = currentUser;
- } else {
- item["Author"] = systemUser;
- item["Editor"] = systemUser;
- }
- item.Update();
- );
- }
The code can access the RestrictedList that the current user can't access normally. This prevents the user from accessing the list by entering the URL directly.
Within the delegate, a new list item is created. The IsAnonymous parameter determines whether the Author and Editor list fields take the system account or the current user.
Using the preceding way:- You can decide to set the user's data or leave the item in an anonymous state.
- You can manipulate the Author and Editor field values as required.
2. Using SPUserToken(Preffered Way)
Another way to elevate the privilege or to impersonate is by using an SPUserToken object. The first step is to get the token for the user to be impersonated in SharePoint using SPUser.UserToken. Then use this token to the SPSite constructor to create a new impersonated security context.
This is the most recommend way and the best practice to perform impersonation in the context of SharePoint. However, when using SPUserToken, you need to ensure that the user exists of whom you are impersonating and that user has the proper permissions. In production scenarios, you may not know the user in advance and this technique may not work.
The following is the example to impersonate a SHAREPOINTSYSTEM account.
- SPWeb oWeb = SPContext.Current.Web;
- SPUserToken token = oWeb.AllUsers[@"SHAREPOINTSYSTEM"].UserToken;
- using (SPSite Site = new SPSite(oWeb.Site.ID, token))
- {
- using (SPWeb dweb = site.OpenWeb())
- {
- // Perform administrative actions by using the elevated site and web objects.
- // Web.CurrentUser.LoginName gives SHAREPOINTsystem
- // WindowsIdentity.GetCurrent().Name gives current logged-in username i.e. SPContext.Current.Web.CurrentUser.LoginName.
- // Hence,Only SharePoint Security context is changed while Windows Security context is not changed.
- }
- }
When a user request is authenticated, it runs under the context of a SPUser object and carries a security token, SPUserToken. When you create a SPSite, an instance of the SPUserToken and the SPUser are also created. When your code attempts to access resources inside SharePoint, this user's security token is checked against ACLs to determine whether it should grant or deny access.
It is important to understand a few important aspects when using the SPUserToken approach.
- Windows Security Context is not changed
If you see the code above, WindowsIdentity.GetCurrent().Name is the same as the Name of the current user making the request which is SPContext.Current.Web.CurrentUser.
So any call to external systems like DB or WebServices will be made by the Windows account of the current user. It succeeds or not depending on the permissions that the user has on that external system.
[Note: If the external system is on another server, calls will not succeed even if the current user has permissions. This is due to a double-hop issue in NTLM authentication. You need to configure KERBEROS authentication for that or use Secure Store Service.
- Tokens have expiry time
The tokens time out after 24 hours, so they can be used in the code that needs to impersonate users in the case of workflow actions or asynchronous event receivers occurring within 24 hours. fter the SPUserToken object is returned to the caller, it is the caller's responsibility to not use the token after it is expired.
[Note: The token timeout value can be set using the Windows PowerShell console or stsadm as in the following:
tsadm -o setproperty -propertyname token-timeout -propertyvalue 720
]
- Using Win32 API
We see that none of the preceding ways, SPUserToken and RunWithelevatedPrivileges, work when we need to impersonate systems outside SharePoint like Console or Windows Applications.
If you have Console or Windows Application and want to impersonate the call to a arePoint web service or Object Model, you must programmatically create a WindowsIdentity object for the caller. Create a WindowsIdentity object either by using a logon token returned from the Win32 LogonUser API or by using the WindowsIdentity(userPrincipalName) constructor that takes a single parameter of a user principal name (UPN).
Pease note that you should avoid using this technique in SharePoint components like webpartsor application pages.
There are other ways of impersonation which is not so common but I see people use it sometimes (as used here).- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using Microsoft.SharePoint;
- using System.Threading;
- using System.Web;
- using System.Security.Principal;
- using System.Runtime.InteropServices;
- namespace AmitKumawat.ConsoleApp
- {
- class Program
- {
- // Declare signatures for Win32 LogonUser and CloseHandle APIs
- [DllImport("advapi32.dll", SetLastError = true)]
- static extern bool LogonUser(
- string principal,
- string authority,
- string password,
- LogonSessionType logonType,
- LogonProvider logonProvider,
- out IntPtr token);
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern bool CloseHandle(IntPtr handle);
- enum LogonSessionType : uint
- {
- Interactive = 2,
- Network,
- Batch,
- Service,
- NetworkCleartext = 8,
- NewCredentials
- }
- enum LogonProvider : uint
- {
- Default = 0, // default for platform (use this!)
- WinNT35, // sends smoke signals to authority
- WinNT40, // uses NTLM
- WinNT50 // negotiates Kerb or NTLM
- }
- static void Main(string[] args)
- {
- string username;
- string password;
- string domain;
- Console.Write("Enter username:");
- username = Console.ReadLine();
- Console.Write("Enter domain:");
- domain = Console.ReadLine();
- Console.Write("Enter password: ");
- password = Console.ReadLine();
- IntPtr token = IntPtr.Zero;
- WindowsImpersonationContext impersonatedUser = null;
- try
- {
- bool result = LogonUser(username, domain, password, LogonSessionType.Network, LogonProvider.Default, out token);
- if (result)
- {
- WindowsIdentity id = new WindowsIdentity(token);
- // Begin impersonation
- impersonatedUser = id.Impersonate();
- Console.WriteLine("Identity after impersonation : " + WindowsIdentity.GetCurrent().Name);
- // Call to Sharepoint Web services or object model
- }
- else
- {
- Console.WriteLine("LogonUser failed: " + Marshal.GetLastWin32Error().ToString());
- }
- }
- catch
- {
- }
- finally
- {
- // Stop impersonation and revert to the process identity
- if (impersonatedUser != null)
- impersonatedUser.Undo();
- // Free the token
- if (token != IntPtr.Zero)
- CloseHandle(token);
- }
- // Verify the old process identity
- Console.WriteLine(String.Format("Identity after Undo: " + WindowsIdentity.GetCurrent().Name));
- Console.Read();
- }
- }
- Windows API RevertToSelf function .
- Impersonate(IntPtr) method with zero passed as the parameter.
These ways are not recommended for the components inside SharePoint like Web parts or Pages. The following is the reason why.
We now know that the SharePoint request must impersonate the calling user's identity (<identity impersonate=”true” /> in web.config). SharePoint web applications are configured to impersonate the calling user automatically. If you try to suspend this impersonation by using above ways, your code may fail or behave abnormally.
Summary
Let's summarize the important points from above discussion.
- You should avoid using SPSecurity.RunWithElevatedPrivileges for an elevation of privilege of SharePoint objects. Instead, use SPUserToken to impersonate SPSite with a specific account, as shown previously. If you want to make network calls under the application pool identity or you don't have a valid and known SPUser to retrieve SPUsertoken then SPSecurity.RunWithElevatedPrivileges is the only choice.
- If you need to use SPSecurity.RunWithElevatedPrivileges, it is a must to dispose of all objects in the block. Do not pass SharePoint objects out of the RunWithElevatedPrivileges block.
- If you want to impersonate in an application outside SharePoint, the only option is to use the Windows API or WindowsIdentity.Impersonate(token).

Aneesh BhargavanPosted Nov 9, 2010, 1:32 AM
Hi Destin, Thank you for you time, this was what I was look for :) Could you please also explain what is the difference between Impersonisation and Run With elevated privilages? Thanks Aneesh