2

I want to issue a search query from code using KeywordQuery as a specific user. The background is that item level security needs to be applied. I want security trimmed search results.

I tried two things so far:

  1. creating KeywordQuery with SPSite that has been created using a user token
  2. executing query as impersonated user

Both approaches failed. The first one gives me all search results and seems to ignore the user I created the SPSite with.

The second approach looks like this:

var impersonatedIdentity = new WindowsIdentity("user@domain");
var wic = impersonatedIdentity.Impersonate();
[ query here ]
wic.Undo();

It gives an error: System.Data.SqlClient.SqlException : A transport-level error has occurred when sending the request to the server. (provider: Shared Memory Provider, error: 0 - Either a required impersonation level was not provided, or the provided impersonation level is invalid.).

I found a quote from Corey Roth here, saying:

The KeywordQuery class uses the credentials of the current user. The documents you see will be security trimmed down to the specific user.

This sounds like my second approach. But it doesn't work.

Does anybody know how to query for search results as specific user?

2 Answers 2

3

It is not required to know the password in 2010 & 2013 to impersonate the user for search. You can simply do the following:

Helper classes:

public class WindowsImpersonation : IDisposable {

    public readonly bool impersonated = false;

    private WindowsImpersonationContext wic  = null;

    //upn is the user account in the form of [email protected]
    //ntAccount is not required but is used to prevent unnecessary impersonation
    public WindowsImpersonation(string upn, string ntAccount = "") {

        //see if impersonation is required
        WindowsIdentity current = WindowsIdentity.GetCurrent();
        if ( current != null && current.Name.Equals(ntAccount, StringComparison.OrdinalIgnoreCase) ) {
            return; //nope
        } 

        try {

            //
            // current user account must have the following permissions:
            //      - Act as Part of the Operating System
            //      - Impersonate a client after authentication
            //
            //  gpedit.msc -> local computer -> Windows Settings -> Security Settings -> Local Policies -> User Rights Assignment


            //impersonate user
            wic = (new WindowsIdentity(upn)).Impersonate();
            impersonated = true;
        } catch(Exception) {
            Dispose();
            throw;
        }

    }


    public void Dispose() {
        if (wic != null) {
            wic.Undo();
            wic = null;
        } 
    }

}

public static class Extensions_User_Impersonate {

     public static IDisposable Impersonate(this User user) {
         return new WindowsImpersonation(user.upn, user.ntAccount);
     }

}

Example:

//elevate, impersonate and search:
    //HostingEnvironment.Impersonate will not work in 2013.
SPSecurity.RunWithElevatedPrivileges(() => {
    using (user.Impersonate()) {
        //Search code goes here
    }
});

(some associated classes not included and some comments redacted)

2
  • I tried this in my second approach and it failed. If it works for you it must have been something specific to my environment or something about the order of commands I used. I experimented a lot. Good to know it should work. Nov 7, 2013 at 10:24
  • 1
    Make sure you set the permission GPOs on all the WFEs/APP servers and elevate to the app pool account otherwise it won't work. I think error is the one i saw when the GPOs weren't set as i recognize it but i'm not sure. This is currently working on over 30 different production farms running a variety of different configurations (2010 & 2013)
    – katbyte
    Nov 7, 2013 at 23:18
1

I found the solution. Impersonating the user the way I did it was not enough. You actually have to log in or get the token of an already logged in user.

Example for logging in:

IntPtr token = IntPtr.Zero;
LogonUser("user@domain", null, "password", LogonSessionType.Network, LogonProvider.Default, out token);
var impersonatedIdentity = new WindowsIdentity(token);
var wic = impersonatedIdentity.Impersonate();

[ query here ]

wic.Undo();
CloseHandle(token);

(Inspiration taken from this blog post showing different ways to impersonate. You can also find the imports of LogonUser and CloseHandle there as well as the used enums.)

The SqlException is gone and the search results are security trimmed for the impersonated user.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.