I'm trying to documents of a certain content type that has a certain tagging (the documents are spread over several site collection). To get some performance I'm trying to do it with a KeywordQuery.

The problem I have is that the user executing the code might not have access to all documents, but the check must still be made on all documents. Therefore I have tried to run the query in an elevated mode:

SPSecurity.RunWithElevatedPrivileges(delegate()
{
    using (SPSite eSite = new SPSite(siteUrl))
    {
        KeywordQuery keywordQuery = new KeywordQuery(eSite);
        keywordQuery.QueryText = "Some search query";
        keywordQuery.TrimDuplicates = false;
        SearchExecutor searchExecutor = new SearchExecutor();
        ResultTableCollection resultTableCollection = searchExecutor.ExecuteQuery(keywordQuery);
        var resultTables = resultTableCollection.Filter("TableType", KnownTableTypes.RelevantResults);
        resultTable = resultTables.FirstOrDefault();
    }
});

This code returns an error

Operation is not valid due to the current state of the object

on this row KeywordQuery keywordQuery = new KeywordQuery(eSite);

Then I tried opening up the SPSite with the user token of the system account, like this:

using (SPSite scSite = new SPSite(siteCollection.Url, SPUserToken.SystemAccount))
{
     KeywordQuery keywordQuery = new KeywordQuery(scSite);
     keywordQuery.QueryText = "Some query"
     keywordQuery.TrimDuplicates = false;
     SearchExecutor searchExecutor = new SearchExecutor();
     ResultTableCollection resultTableCollection = searchExecutor.ExecuteQuery(keywordQuery);
     var resultTables = resultTableCollection.Filter("TableType", KnownTableTypes.RelevantResults);
     resultTable = resultTables.FirstOrDefault();
}

This throws no error, but returns results only for the logged in user (not the system account).

Is it possible to execute a KeywordQuery in elevation, or without security trimming, without using some hardcore impersonation?

有帮助吗?

解决方案

I think @LeopoldLerch is on to the correct approach.

Try doing the following

Guid siteId = SPContext.Current.Site.ID;
SPUserToken systemAccountUserToken = SPContext.Current.Site.SystemAccount.UserToken;
HttpContext currentUserContext = HttpContext.Current;

if (SPContext.Current != null)
    HttpContext.Current = null;

SPSecurity.RunWithElevatedPrivileges(() =>
{
    using (SPSite elevatedSite = new SPSite(siteId, systemAccountUserToken))
    {
        HttpRequest httpRequest = new HttpRequest(string.Empty, elevatedSite.RootWeb.Url, string.Empty);
        HttpContext.Current = new HttpContext(httpRequest, new HttpResponse(new StringWriter()));
        SPControl.SetContextWeb(HttpContext.Current, elevatedSite.RootWeb);

        KeywordQuery keywordQuery = new KeywordQuery(elevatedSite);
        keywordQuery.QueryText = "Some query"
        keywordQuery.TrimDuplicates = false;
        // And then try setting SiteContext as well. Might not be necessary, but try it.
        keywordQuery.SiteContext = new Uri(elevatedSite.Url);
        SearchExecutor searchExecutor = new SearchExecutor();
        ResultTableCollection resultTableCollection = searchExecutor.ExecuteQuery(keywordQuery);
        var resultTables = resultTableCollection.Filter("TableType", KnownTableTypes.RelevantResults);
        resultTable = resultTables.FirstOrDefault();
    }
});

HttpContext.Current = currentUserContext;

其他提示

Try to delete the current httpcontext before elevating (but do not forget to remember it and set it back

like

var storedContext = HttpContext.Current;
HttpContext.Current = null;
<Do something>
HttpContext.Current = storedContext;

Because it seems to use still your curentUsers Credentials when calling the search service (which it still does in the background). This trick works also in similar cases;

许可以下: CC-BY-SA归因
scroll top