/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.blur.lucene.security.search;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import org.apache.blur.lucene.security.index.AccessControlFactory;
import org.apache.blur.lucene.security.index.AccessControlReader;
import org.apache.blur.lucene.security.index.SecureAtomicReader;
import org.apache.blur.lucene.security.index.SecureDirectoryReader;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.index.StoredFieldVisitor;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Weight;
public class SecureIndexSearcher extends IndexSearcher {
private final IndexReader _secureIndexReader;
private final Map<Object, AtomicReaderContext> _leaveMap;
private final AccessControlFactory _accessControlFactory;
private final Collection<String> _readAuthorizations;
private final Collection<String> _discoverAuthorizations;
private final Set<String> _discoverableFields;
private final String _defaultReadMaskMessage;
private AccessControlReader _accessControlReader;
public SecureIndexSearcher(IndexReader r, AccessControlFactory accessControlFactory,
Collection<String> readAuthorizations, Collection<String> discoverAuthorizations, Set<String> discoverableFields,
String defaultReadMaskMessage) throws IOException {
this(r, null, accessControlFactory, readAuthorizations, discoverAuthorizations, discoverableFields,
defaultReadMaskMessage);
}
public SecureIndexSearcher(IndexReader r, ExecutorService executor, AccessControlFactory accessControlFactory,
Collection<String> readAuthorizations, Collection<String> discoverAuthorizations, Set<String> discoverableFields,
String defaultReadMaskMessage) throws IOException {
this(r.getContext(), executor, accessControlFactory, readAuthorizations, discoverAuthorizations,
discoverableFields, defaultReadMaskMessage);
}
public SecureIndexSearcher(IndexReaderContext context, AccessControlFactory accessControlFactory,
Collection<String> readAuthorizations, Collection<String> discoverAuthorizations, Set<String> discoverableFields,
String defaultReadMaskMessage) throws IOException {
this(context, null, accessControlFactory, readAuthorizations, discoverAuthorizations, discoverableFields,
defaultReadMaskMessage);
}
public SecureIndexSearcher(IndexReaderContext context, ExecutorService executor,
AccessControlFactory accessControlFactory, Collection<String> readAuthorizations,
Collection<String> discoverAuthorizations, Set<String> discoverableFields, String defaultReadMaskMessage)
throws IOException {
super(context, executor);
_accessControlFactory = accessControlFactory;
_readAuthorizations = readAuthorizations;
_discoverAuthorizations = discoverAuthorizations;
_discoverableFields = discoverableFields;
_defaultReadMaskMessage = defaultReadMaskMessage;
_accessControlReader = _accessControlFactory.getReader(readAuthorizations, discoverAuthorizations,
discoverableFields, _defaultReadMaskMessage);
_secureIndexReader = getSecureIndexReader(context);
List<AtomicReaderContext> leaves = _secureIndexReader.leaves();
_leaveMap = new HashMap<Object, AtomicReaderContext>();
for (AtomicReaderContext atomicReaderContext : leaves) {
AtomicReader atomicReader = atomicReaderContext.reader();
SecureAtomicReader secureAtomicReader = (SecureAtomicReader) atomicReader;
AtomicReader originalReader = secureAtomicReader.getOriginalReader();
Object coreCacheKey = originalReader.getCoreCacheKey();
_leaveMap.put(coreCacheKey, atomicReaderContext);
}
}
protected AtomicReader getSecureAtomicReader(AtomicReader atomicReader) throws IOException {
return SecureAtomicReader.create(_accessControlFactory, atomicReader, _readAuthorizations, _discoverAuthorizations,
_discoverableFields, _defaultReadMaskMessage);
}
protected IndexReader getSecureIndexReader(IndexReaderContext context) throws IOException {
IndexReader indexReader = context.reader();
if (indexReader instanceof DirectoryReader) {
return SecureDirectoryReader.create(_accessControlFactory, (DirectoryReader) indexReader, _readAuthorizations,
_discoverAuthorizations, _discoverableFields, _defaultReadMaskMessage);
} else if (indexReader instanceof AtomicReader) {
return SecureAtomicReader.create(_accessControlFactory, (AtomicReader) indexReader, _readAuthorizations,
_discoverAuthorizations, _discoverableFields, _defaultReadMaskMessage);
}
throw new IOException("IndexReader type [" + indexReader.getClass() + "] not supported.");
}
@Override
public IndexReader getIndexReader() {
return _secureIndexReader;
}
protected Filter getSecureFilter() throws IOException {
return _accessControlReader.getQueryFilter();
}
protected Collector getSecureCollector(final Collector collector) {
return new Collector() {
@Override
public void setScorer(Scorer scorer) throws IOException {
collector.setScorer(scorer);
}
@Override
public void setNextReader(AtomicReaderContext context) throws IOException {
Object key = context.reader().getCoreCacheKey();
AtomicReaderContext atomicReaderContext = _leaveMap.get(key);
collector.setNextReader(atomicReaderContext);
}
@Override
public void collect(int doc) throws IOException {
collector.collect(doc);
}
@Override
public boolean acceptsDocsOutOfOrder() {
return collector.acceptsDocsOutOfOrder();
}
};
}
@Override
public Weight createNormalizedWeight(Query query) throws IOException {
return super.createNormalizedWeight(wrapFilter(query, getSecureFilter()));
}
@Override
protected void search(List<AtomicReaderContext> leaves, Weight weight, Collector collector) throws IOException {
super.search(leaves, weight, getSecureCollector(collector));
}
public Document doc(int docID) throws IOException {
return _secureIndexReader.document(docID);
}
public void doc(int docID, StoredFieldVisitor fieldVisitor) throws IOException {
_secureIndexReader.document(docID, fieldVisitor);
}
public Document doc(int docID, Set<String> fieldsToLoad) throws IOException {
return _secureIndexReader.document(docID, fieldsToLoad);
}
}