Windows Authentication and no, not going across domains.
I can see where the ExecuteQueryTask should be logging identity information, but I can't find it in my logs even though I've set the trace log level to VerboseEx for all the Search categories. Anyway, I will have to look into that...
It may very well be related to authentication, but the correct current identity (via Thread.CurrentPrincipal) is returning "true" for IsAuthenticated, so it's not reverting because of an authentication failure. I believe it's reverting because
it's somehow losing the WindowsIdentity of the logged in user due to the new task starting up.
I can replicate this if I change my handler to execute multiple queries asynchronously via Task.Wait in the same way that ExecuteQueries does, as follows:
public class SPSearchTest : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
string masterQuery = "";
if (context.Request.QueryString["searchString"] != null)
masterQuery = context.Request.QueryString["searchString"];
context.Response.Write(String.Format("Thread.CurrentPrincipal: {0}, Windows.Identity: {1}, HttpContext.CurrentUser: {2}", Thread.CurrentPrincipal.Identity.Name, WindowsIdentity.GetCurrent().Name, context.User.Identity.Name));
SPSite searchSite = SPContext.Current.Site;
string jsonObj = "";
Dictionary<string, KeywordQuery> allQueries = new Dictionary<string, KeywordQuery>();
KeywordQuery docQuery = new KeywordQuery(searchSite);
docQuery.QueryText = masterQuery + " AND SPContentType=Document";
allQueries.Add("Documents", docQuery);
KeywordQuery eventQuery = new KeywordQuery(searchSite);
eventQuery.QueryText = masterQuery + " AND SPContentType=Event";
allQueries.Add("Events", eventQuery);
Dictionary<string, Task<ResultTableCollection>> queryTasks = new Dictionary<string, Task<ResultTableCollection>>();
foreach (KeyValuePair<string, KeywordQuery> kvp in allQueries)
{
Task<ResultTableCollection> task = Task<ResultTableCollection>.Factory.StartNew(
() => DoSearch(context, WindowsIdentity.GetCurrent(), kvp.Key, kvp.Value)
);
queryTasks.Add(kvp.Key, task);
}
Task.WaitAll(queryTasks.Values.ToArray<Task<ResultTableCollection>>());
Dictionary<string,ResultTableCollection> resultTableCollection = new Dictionary<string, ResultTableCollection>();
foreach (KeyValuePair<string, Task<ResultTableCollection>> pair in queryTasks)
{
Task<ResultTableCollection> task = pair.Value;
if (task.Status != TaskStatus.Faulted)
{
resultTableCollection.Add(pair.Key, task.Result);
}
}
IEnumerable<ResultTable> resultTables = resultTableCollection["Documents"].Filter("TableType", KnownTableTypes.RelevantResults);
ResultTable resultTable = resultTables.FirstOrDefault();
DataTable results = resultTable.Table;
jsonObj = JsonConvert.SerializeObject(results, Common.serializerSettings);
context.Response.ContentType = "text/plain";
context.Response.Write(jsonObj);
}
private ResultTableCollection DoSearch(HttpContext context, WindowsIdentity windowsIdentity, string scope, KeywordQuery query)
{
if(Thread.CurrentPrincipal != null)
context.Response.Write(String.Format(" Scope is {0} and Thread.CurrentPrincipal is {1}. ", scope, Thread.CurrentPrincipal.Identity.Name));
else
context.Response.Write(String.Format(" Scope is {0} and Thread.CurrentPrincipal is null. ", scope));
if (WindowsIdentity.GetCurrent() != null)
context.Response.Write(String.Format(" Scope is {0} and Windows.Identity is {1}. ", scope, WindowsIdentity.GetCurrent().Name));
else
context.Response.Write(String.Format(" Scope is {0} and Windows.Identity is null. ", scope));
WindowsImpersonationContext wic = null;
if (!Thread.CurrentPrincipal.Identity.IsAuthenticated)
{
context.Response.Write(String.Format(" Thread.CurrentPrincipal.Identity is not authenticated for {0} scope. ", scope));
if (windowsIdentity != null)
wic = windowsIdentity.Impersonate();
else
context.Response.Write(String.Format(" Scope is {0} and cannot set Windows.Identity because variable is null. ", scope));
}
else
context.Response.Write(String.Format(" Thread.CurrentPrincipal.Identity IS authenticated for {0} scope. If it was not, the WIC would have been set to {1}. ", scope, windowsIdentity.Name));
if (Thread.CurrentPrincipal != null)
context.Response.Write(String.Format(" After doing stuff - Scope is {0} and Thread.CurrentPrincipal is {1}. ", scope, Thread.CurrentPrincipal.Identity.Name));
else
context.Response.Write(String.Format(" After doing stuff - Scope is {0} and Thread.CurrentPrincipal is null. ", scope));
if (WindowsIdentity.GetCurrent() != null)
context.Response.Write(String.Format(" Scope is {0} and Windows.Identity is {1}. ", scope, WindowsIdentity.GetCurrent().Name));
else
context.Response.Write(String.Format(" Scope is {0} and Windows.Identity is null. ", scope));
SearchExecutor s = new SearchExecutor();
return s.ExecuteQuery(query);
}
}
The Thread.CurrentPrincipal passes the authentication test, but the value returned by WindowsIdentity.GetCurrent() changes to the search service account within the "DoSearch" task.