Performing a search that is not security trimmed
-
04-10-2020 - |
题
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;