Returning approved SharePoint list items from a console application
If you are using the SharePoint object model to return list items, the items are security trimmed based on the rights of the user running the code. In most cases this is great, you don’t have to do any manipulation of the items returned from CAML queries to ensure users only see what they have access to.
If you are running object model code from within a console/windows application, however, the code must be running with some serious permissions including being a site collection administrator. I recently had a situation where I wanted to return only approved items in queries from lists within the site (i.e. only items an anonymous user would see). Thankfully it wasn’t too difficult, but there are a couple of steps to get this going.
The first is to ensure permissions on the lists are set appropriately. This can be done by navigating to Settings > List Settings > Versioning Settings from a list. You will need to ensure content approval is on, and draft item security is restricted to either editors or approvers. This will ensure draft versions do not appear in public views.
The second thing to do is to impersonate a user with reduced privileges while you run the object model queries from within the console/windows application. An example is given below:
Impersonation code:
public class Impersonation
{
public Impersonation()
{
}
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsImpersonationContext impersonationContext;
[DllImport(“advapi32.dll”)]
private static extern int LogonUserA(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport(“advapi32.dll”, CharSet = CharSet.Auto, SetLastError = true)]
private static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport(“advapi32.dll”, CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RevertToSelf();
[DllImport(“kernel32.dll”, CharSet = CharSet.Auto)]
private static extern bool CloseHandle(IntPtr handle);
/// impersonateValidUser
public bool impersonateValidUser(String userName, String domain, String password)
{
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
return false;
}
/// impersonates the reward search user
public bool impersonateSearchUser()
{
Dictionary<string, string> accountSettings = LoadConfiguration(“SearchUser”);
// impersonate user
return impersonateValidUser(
accountSettings[“Username”],
accountSettings[“Domain”],
accountSettings[“Password”]);
}
/// Loads a connection string as key/value pairs
private Dictionary<string, string> LoadConfiguration(string connectionString)
{
string accountString = ConfigurationManager.ConnectionStrings[connectionString].ConnectionString;
Dictionary<string, string> config = new Dictionary<string, string>();
foreach (string setting in accountString.Split(“;”.ToCharArray()))
{
string[] pairs = setting.Split(“=”.ToCharArray());
config.Add(pairs[0], pairs[1]);
}
return config;
}
/// undoImpersonation
public void undoImpersonation()
{
impersonationContext.Undo();
}
}
Then you can use the impersonation code to return list items as the specified user as shown below:
SPListItemCollection items;
Impersonation impersonationContext = new Impersonation();
impersonationContext.impersonateDocsShareUser();
using (SPSite site = new SPSite(“http://moss.co.nz”))
{
using (SPWeb web = site.OpenWeb())
{
SPList list = web.Lists[“Category”];
SPQuery q = new SPQuery();
q.ViewFields = “”;
items = list.GetItems(q);
}
}
As the impersonation account details are stored in a configuration file you can easily encrypt these using RSA.
Becky,
I believe he's using impersonation to replicate the security trimming that you get by default with the web UI.
I've been working on this via web services and I just throw this in my CAML query — no impersonation necessary:
<Where>
<Eq>
<FieldRef Name="_ModerationStatus" />
<Value Type="ModStat">Approved</Value>
</Eq>
</Where>
Corey Cole
8 Nov 07 at 4:31 pm
If you run the console application off of Windows Scheduler as the Site Administrator, then you don't need to use impersonation. I guess it all depends on what you want to do with the console app.
Becky Isserman
8 Nov 07 at 2:35 pm