/**
 * Copyright 2008 Matthew Hillsdon
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.hillsdon.reviki.search.impl;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import net.hillsdon.reviki.configuration.DeploymentConfiguration;
import net.hillsdon.reviki.configuration.WikiConfiguration;
import net.hillsdon.reviki.search.QuerySyntaxException;
import net.hillsdon.reviki.search.SearchEngine;
import net.hillsdon.reviki.search.SearchMatch;
import net.hillsdon.reviki.vc.AutoProperties;
import net.hillsdon.reviki.vc.PageInfo;
import net.hillsdon.reviki.vc.PageStoreAuthenticationException;
import net.hillsdon.reviki.vc.PageStoreException;
import net.hillsdon.reviki.vc.impl.AutoPropertiesApplierImpl;
import net.hillsdon.reviki.web.vcintegration.BasicAuthPassThroughBasicSVNOperationsFactory;
import net.hillsdon.reviki.web.vcintegration.RequestLifecycleAware;
import net.hillsdon.reviki.web.vcintegration.RequestScopedThreadLocalBasicSVNOperations;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Filter search results so that only pages from wikis that your user can view are returned: https://jira.int.corefiling.com/browse/REVIKI-523
 *
 * @author swhite
 */
public class BasicAuthAwareSearchEngine implements SearchEngine {

  private static final Log LOG = LogFactory.getLog(BasicAuthAwareSearchEngine.class);

  private final SearchEngine _delegate;
  private final DeploymentConfiguration _config;
  private final ThreadLocal<HttpServletRequest> _request = new ThreadLocal<HttpServletRequest>();

  public BasicAuthAwareSearchEngine(final SearchEngine delegate, final DeploymentConfiguration config) {
    _delegate = delegate;
    _config = config;
  }

  public RequestLifecycleAware getRequestLifecycleAware() {
    return new RequestLifecycleAware() {
      @Override
      public void destroy() {
        _request.set(null);
      }
      
      @Override
      public void create(HttpServletRequest request) {
        _request.set(request);
      }
    };
  }
  public void index(final PageInfo page, boolean buildingIndex) throws IOException, PageStoreException {
    _delegate.index(page, buildingIndex);
  }

  public Set<SearchMatch> search(final String query, final boolean provideExtracts, boolean singleWiki) throws IOException, QuerySyntaxException, PageStoreException {
	  final Map<String, Boolean> wikiAccessOkCache = new LinkedHashMap<String, Boolean>();
    Set<SearchMatch> results = new LinkedHashSet<SearchMatch>(); 
    results.addAll(_delegate.search(query, provideExtracts, singleWiki));
    CollectionUtils.filter(results, new Predicate() {
      @Override
      public boolean evaluate(final Object o) {
        final SearchMatch match = (SearchMatch) o;
        Boolean accessOk = wikiAccessOkCache.get(match.getWiki());
        if (accessOk == null) {
          try {
            WikiConfiguration configuration = _config.getConfiguration(match.getWiki());
            RequestScopedThreadLocalBasicSVNOperations operations = new RequestScopedThreadLocalBasicSVNOperations(new BasicAuthPassThroughBasicSVNOperationsFactory(configuration.getUrl(), new AutoPropertiesApplierImpl(new AutoProperties() {
              public Map<String, String> read() {
                return new LinkedHashMap<String, String>();
              }
            })));
            operations.create(_request.get());
            try {
              operations.checkPath(match.getPage(), -1);
            }
            finally {
              operations.destroy();
            }
            accessOk = true;
          }
          catch (PageStoreAuthenticationException ex) {
            accessOk = false;
          }
          catch (PageStoreException ex) { 
            LOG.error("Exception determining access to wiki: " + match.getWiki(), ex);
            return false;
          }
          wikiAccessOkCache.put(match.getWiki(), accessOk);
          LOG.debug("access to results in " + match.getWiki() + ": " + accessOk);
        }
        return accessOk;
      }
    });
    return results;
  }

  public long getHighestSyncedRevision() throws IOException {
    return _delegate.getHighestIndexedRevision();
  }

  public long getHighestIndexedRevision() throws IOException {
    return _delegate.getHighestIndexedRevision();
  }

  public void rememberHighestIndexedRevision(long revision) throws IOException {
    _delegate.rememberHighestIndexedRevision(revision);
  }

  public boolean isIndexBeingBuilt() throws IOException {
    return _delegate.isIndexBeingBuilt();
  }

  public void setIndexBeingBuilt(boolean buildingIndex) throws IOException {
    _delegate.setIndexBeingBuilt(buildingIndex);
  }

  public void delete(final String wiki, final String path, boolean buildingIndex) throws IOException {
    _delegate.delete(wiki, path, buildingIndex);
  }

  public String escape(final String in) {
    return _delegate.escape(in);
  }

  public Set<String> incomingLinks(final String page) throws IOException, PageStoreException {
    return _delegate.incomingLinks(page);
  }

  public Set<String> outgoingLinks(final String page) throws IOException, PageStoreException {
    return _delegate.outgoingLinks(page);
  }
}

@@ -3593,24 +3593,400 @@
  == null) %7B%0A
+          // Determine if the user is allowed access to the matched wiki, based on:%0A          // * Whether or not a username and password are required to index the wiki, if not then assume everyone is allowed access %0A          // * If a username is required then determine if the current user has access.  This is slower, hence the short circuit described in the first point.%0A
           tr
@@ -4070,32 +4070,92 @@
 tch.getWiki());%0A
+            if (!%22%22.equals(configuration.getSVNUser())) %7B%0A  
             Requ
@@ -4390,24 +4390,26 @@
             
+  
 public Map%3CS
@@ -4428,24 +4428,26 @@
 g%3E read() %7B%0A
+  
             
@@ -4500,32 +4500,34 @@
 ;%0A              
+  
 %7D%0A            %7D)
@@ -4528,14 +4528,18 @@
     
+ 
+ 
 %7D)));%0A
+  
     
@@ -4585,38 +4585,42 @@
 ));%0A            
+ 
+ 
 try %7B%0A
+  
               op
@@ -4664,34 +4664,38 @@
 1);%0A            
+ 
+ 
 %7D%0A
+  
             fina
@@ -4706,32 +4706,34 @@
 %7B%0A              
+  
 operations.destr
@@ -4734,24 +4734,40 @@
 .destroy();%0A
+              %7D%0A
             

Improve performance of the fix for https://jira.int.corefiling.com/browse/REVIKI-523 based on assuming that all users have access to wikis for which no username and password are configured for indexing.