incubator-blur-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From amccu...@apache.org
Subject [2/3] git commit: Fixed BLUR-434
Date Mon, 15 Jun 2015 13:39:28 GMT
http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlFactory.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlFactory.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlFactory.java
new file mode 100644
index 0000000..25ad35f
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlFactory.java
@@ -0,0 +1,32 @@
+/**
+ * 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.index;
+
+import java.util.Collection;
+import java.util.Set;
+
+public abstract class AccessControlFactory {
+  
+  public abstract String getDiscoverFieldName();
+  
+  public abstract String getReadFieldName();
+
+  public abstract AccessControlWriter getWriter();
+
+  public abstract AccessControlReader getReader(Collection<String> readAuthorizations, Collection<String> discoverAuthorizations,
+      Set<String> discoverableFields);
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlReader.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlReader.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlReader.java
new file mode 100644
index 0000000..51b554a
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlReader.java
@@ -0,0 +1,57 @@
+/**
+ * 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.index;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.AtomicReader;
+import org.apache.lucene.search.Filter;
+
+public abstract class AccessControlReader implements Cloneable {
+
+  public final boolean hasAccess(ReadType type, int docID) throws IOException {
+    switch (type) {
+    case DOCS_ENUM:
+    case LIVEDOCS:
+      return readOrDiscoverAccess(docID);
+    case DOCUMENT_FETCH_DISCOVER:
+      return discoverAccess(docID);
+    case BINARY_DOC_VALUE:
+    case DOCUMENT_FETCH_READ:
+    case NORM_VALUE:
+    case NUMERIC_DOC_VALUE:
+    case SORTED_DOC_VALUE:
+    case SORTED_SET_DOC_VALUE:
+      return readAccess(docID);
+    default:
+      throw new IOException("Unknown type [" + type + "]");
+    }
+  }
+
+  protected abstract boolean readAccess(int docID) throws IOException;
+
+  protected abstract boolean discoverAccess(int docID) throws IOException;
+
+  protected abstract boolean readOrDiscoverAccess(int docID) throws IOException;
+
+  public abstract boolean canDiscoverField(String name) throws IOException;
+
+  public abstract AccessControlReader clone(AtomicReader in) throws IOException;
+
+  public abstract Filter getQueryFilter() throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlWriter.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlWriter.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlWriter.java
new file mode 100644
index 0000000..035cde0
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/AccessControlWriter.java
@@ -0,0 +1,52 @@
+/**
+ * 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.index;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.IndexableField;
+
+public abstract class AccessControlWriter {
+
+  public abstract Iterable<IndexableField> addReadVisiblity(String read, Iterable<IndexableField> fields);
+
+  public abstract Iterable<IndexableField> addDiscoverVisiblity(String discover, Iterable<IndexableField> fields);
+
+  protected Iterable<IndexableField> addField(Iterable<IndexableField> fields, IndexableField... fieldsToAdd) {
+    if (fields instanceof Document) {
+      Document document = (Document) fields;
+      if (fieldsToAdd != null) {
+        for (IndexableField field : fieldsToAdd) {
+          document.add(field);    
+        }
+      }
+      return document;
+    }
+    List<IndexableField> list = new ArrayList<IndexableField>();
+    for (IndexableField indexableField : fields) {
+      list.add(indexableField);
+    }
+    if (fieldsToAdd != null) {
+      for (IndexableField field : fieldsToAdd) {
+        list.add(field);    
+      }
+    }
+    return list;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/DocValueAccessControlFactory.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/DocValueAccessControlFactory.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/DocValueAccessControlFactory.java
new file mode 100644
index 0000000..8c2450e
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/DocValueAccessControlFactory.java
@@ -0,0 +1,262 @@
+/**
+ * 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.index;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.blur.lucene.security.DocumentAuthorizations;
+import org.apache.blur.lucene.security.DocumentVisibility;
+import org.apache.blur.lucene.security.DocumentVisibilityEvaluator;
+import org.apache.lucene.document.SortedDocValuesField;
+import org.apache.lucene.document.StoredField;
+import org.apache.lucene.index.AtomicReader;
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
+
+import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
+
+public class DocValueAccessControlFactory extends AccessControlFactory {
+
+  public static final String DISCOVER_FIELD = "_discover_";
+  public static final String READ_FIELD = "_read_";
+
+  @Override
+  public String getDiscoverFieldName() {
+    return DISCOVER_FIELD;
+  }
+
+  @Override
+  public String getReadFieldName() {
+    return READ_FIELD;
+  }
+
+  @Override
+  public AccessControlWriter getWriter() {
+    return new DocValueAccessControlWriter();
+  }
+
+  @Override
+  public AccessControlReader getReader(Collection<String> readAuthorizations,
+      Collection<String> discoverAuthorizations, Set<String> discoverableFields) {
+    return new DocValueAccessControlReader(readAuthorizations, discoverAuthorizations, discoverableFields);
+  }
+
+  public static class DocValueAccessControlReader extends AccessControlReader {
+
+    private final DocumentAuthorizations _readUnionDiscoverAuthorizations;
+    private final DocumentAuthorizations _readAuthorizations;
+    private final String _readField;
+    private final String _discoverField;
+    private final DocumentVisibilityEvaluator _readUnionDiscoverVisibilityEvaluator;
+    private final DocumentVisibilityEvaluator _readAuthorizationsVisibilityEvaluator;
+    private final ConcurrentLinkedHashMap<Integer, DocumentVisibility> _readOrdToDocumentVisibility;
+    private final ConcurrentLinkedHashMap<Integer, DocumentVisibility> _discoverOrdToDocumentVisibility;
+    private final Set<String> _discoverableFields;
+    private final ThreadLocal<BytesRef> _ref = new ThreadLocal<BytesRef>() {
+      @Override
+      protected BytesRef initialValue() {
+        return new BytesRef();
+      }
+    };
+
+    private SortedDocValues _readFieldSortedDocValues;
+    private SortedDocValues _discoverFieldSortedDocValues;
+
+    public DocValueAccessControlReader(Collection<String> readAuthorizations,
+        Collection<String> discoverAuthorizations, Set<String> discoverableFields) {
+      _discoverableFields = new HashSet<String>(discoverableFields);
+      // TODO need to pass in the discover code to change document if needed
+      List<String> termAuth = new ArrayList<String>();
+      termAuth.addAll(readAuthorizations);
+      termAuth.addAll(discoverAuthorizations);
+      _readUnionDiscoverAuthorizations = new DocumentAuthorizations(termAuth);
+      _readUnionDiscoverVisibilityEvaluator = new DocumentVisibilityEvaluator(_readUnionDiscoverAuthorizations);
+      _readAuthorizations = new DocumentAuthorizations(readAuthorizations);
+      _readAuthorizationsVisibilityEvaluator = new DocumentVisibilityEvaluator(_readAuthorizations);
+      _readField = READ_FIELD;
+      _discoverField = DISCOVER_FIELD;
+      _readOrdToDocumentVisibility = new ConcurrentLinkedHashMap.Builder<Integer, DocumentVisibility>()
+          .maximumWeightedCapacity(1000).build();
+      _discoverOrdToDocumentVisibility = new ConcurrentLinkedHashMap.Builder<Integer, DocumentVisibility>()
+          .maximumWeightedCapacity(1000).build();
+    }
+
+    @Override
+    public AccessControlReader clone(AtomicReader in) throws IOException {
+      try {
+        DocValueAccessControlReader clone = (DocValueAccessControlReader) super.clone();
+        clone._discoverFieldSortedDocValues = in.getSortedDocValues(_discoverField);
+        clone._readFieldSortedDocValues = in.getSortedDocValues(_readField);
+        return clone;
+      } catch (CloneNotSupportedException e) {
+        throw new IOException(e);
+      }
+    }
+
+    private boolean readOrDiscoverAccess(BytesRef ref, int doc) throws IOException {
+      if (readAccess(ref, doc)) {
+        return true;
+      }
+      if (discoverAccess(ref, doc)) {
+        return true;
+      }
+      return false;
+    }
+
+    private boolean discoverAccess(BytesRef ref, int doc) throws IOException {
+      SortedDocValues discoverFieldSortedDocValues = _discoverFieldSortedDocValues;
+      // Checking discovery access
+      int ord = discoverFieldSortedDocValues.getOrd(doc);
+      if (ord >= 0) {
+        // If < 0 means there is no value.
+        DocumentVisibility discoverDocumentVisibility = _discoverOrdToDocumentVisibility.get(ord);
+        if (discoverDocumentVisibility == null) {
+          discoverFieldSortedDocValues.get(doc, ref);
+          discoverDocumentVisibility = new DocumentVisibility(ref.utf8ToString());
+          _discoverOrdToDocumentVisibility.put(ord, discoverDocumentVisibility);
+        }
+        if (_readUnionDiscoverVisibilityEvaluator.evaluate(discoverDocumentVisibility)) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    private boolean readAccess(BytesRef ref, int doc) throws IOException {
+      SortedDocValues readFieldSortedDocValues = _readFieldSortedDocValues;
+      // Checking read access
+      int ord = readFieldSortedDocValues.getOrd(doc);
+      if (ord >= 0) {
+        // If < 0 means there is no value.
+        DocumentVisibility readDocumentVisibility = _readOrdToDocumentVisibility.get(ord);
+        if (readDocumentVisibility == null) {
+          readFieldSortedDocValues.get(doc, ref);
+          readDocumentVisibility = new DocumentVisibility(ref.utf8ToString());
+          _readOrdToDocumentVisibility.put(ord, readDocumentVisibility);
+        }
+
+        if (_readAuthorizationsVisibilityEvaluator.evaluate(readDocumentVisibility)) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    @Override
+    public boolean canDiscoverField(String name) {
+      return _discoverableFields.contains(name);
+    }
+
+    @Override
+    public Filter getQueryFilter() throws IOException {
+      return new Filter() {
+        @Override
+        public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException {
+          AtomicReader reader = context.reader();
+          final int maxDoc = reader.maxDoc();
+          final AccessControlReader accessControlReader = DocValueAccessControlReader.this.clone(reader);
+          return new DocIdSet() {
+            @Override
+            public DocIdSetIterator iterator() throws IOException {
+              return new DocIdSetIterator() {
+
+                private int _docId = -1;
+
+                @Override
+                public int advance(int target) throws IOException {
+                  if (_docId == NO_MORE_DOCS) {
+                    return _docId;
+                  }
+                  for (; target < maxDoc; target++) {
+                    if (accessControlReader.hasAccess(ReadType.QUERY, target)) {
+                      return _docId = target;
+                    }
+                  }
+                  return _docId = NO_MORE_DOCS;
+                }
+
+                @Override
+                public int nextDoc() throws IOException {
+                  return advance(_docId + 1);
+                }
+
+                @Override
+                public int docID() {
+                  return _docId;
+                }
+
+                @Override
+                public long cost() {
+                  return maxDoc;
+                }
+
+              };
+            }
+          };
+        }
+      };
+    }
+
+    @Override
+    protected boolean readAccess(int docID) throws IOException {
+      return readAccess(_ref.get(), docID);
+    }
+
+    @Override
+    protected boolean discoverAccess(int docID) throws IOException {
+      return discoverAccess(_ref.get(), docID);
+    }
+
+    @Override
+    protected boolean readOrDiscoverAccess(int docID) throws IOException {
+      return readOrDiscoverAccess(_ref.get(), docID);
+    }
+
+  }
+
+  public static class DocValueAccessControlWriter extends AccessControlWriter {
+
+    @Override
+    public Iterable<IndexableField> addReadVisiblity(String read, Iterable<IndexableField> fields) {
+      BytesRef value = new BytesRef(read);
+      SortedDocValuesField docValueField = new SortedDocValuesField(READ_FIELD, value);
+      StoredField storedField = new StoredField(READ_FIELD, value);
+      return addField(fields, docValueField, storedField);
+    }
+
+    @Override
+    public Iterable<IndexableField> addDiscoverVisiblity(String discover, Iterable<IndexableField> fields) {
+      BytesRef value = new BytesRef(discover);
+      SortedDocValuesField docValueField = new SortedDocValuesField(DISCOVER_FIELD, value);
+      StoredField storedField = new StoredField(DISCOVER_FIELD, value);
+      return addField(fields, docValueField, storedField);
+    }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/FilterAccessControlFactory.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/FilterAccessControlFactory.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/FilterAccessControlFactory.java
new file mode 100644
index 0000000..038b0c7
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/FilterAccessControlFactory.java
@@ -0,0 +1,288 @@
+/**
+ * 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.index;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Set;
+
+import org.apache.blur.lucene.security.DocumentAuthorizations;
+import org.apache.blur.lucene.security.document.DocumentVisiblityField;
+import org.apache.blur.lucene.security.search.BitSetDocumentVisibilityFilterCacheStrategy;
+import org.apache.blur.lucene.security.search.DocumentVisibilityFilter;
+import org.apache.blur.lucene.security.search.DocumentVisibilityFilterCacheStrategy;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.index.AtomicReader;
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.util.Bits;
+
+public class FilterAccessControlFactory extends AccessControlFactory {
+
+  public static final String DISCOVER_FIELD = "_discover_";
+  public static final String READ_FIELD = "_read_";
+
+  @Override
+  public String getDiscoverFieldName() {
+    return DISCOVER_FIELD;
+  }
+
+  @Override
+  public String getReadFieldName() {
+    return READ_FIELD;
+  }
+
+  @Override
+  public AccessControlWriter getWriter() {
+    return new FilterAccessControlWriter();
+  }
+
+  @Override
+  public AccessControlReader getReader(Collection<String> readAuthorizations,
+      Collection<String> discoverAuthorizations, Set<String> discoverableFields) {
+    return new FilterAccessControlReader(readAuthorizations, discoverAuthorizations, discoverableFields);
+  }
+
+  public static class FilterAccessControlReader extends AccessControlReader {
+
+    private final Set<String> _discoverableFields;
+    private final DocumentVisibilityFilter _readDocumentVisibilityFilter;
+    private final DocumentVisibilityFilter _discoverDocumentVisibilityFilter;
+    private final DocumentVisibilityFilterCacheStrategy _filterCacheStrategy;
+
+    private Bits _readBits;
+    private Bits _discoverBits;
+    private boolean _noReadAccess;
+    private boolean _noDiscoverAccess;
+    private DocIdSet _readDocIdSet;
+    private DocIdSet _discoverDocIdSet;
+    private boolean _isClone;
+
+    public FilterAccessControlReader(Collection<String> readAuthorizations, Collection<String> discoverAuthorizations,
+        Set<String> discoverableFields) {
+      this(readAuthorizations, discoverAuthorizations, discoverableFields,
+          BitSetDocumentVisibilityFilterCacheStrategy.INSTANCE);
+    }
+
+    public FilterAccessControlReader(Collection<String> readAuthorizations, Collection<String> discoverAuthorizations,
+        Set<String> discoverableFields, DocumentVisibilityFilterCacheStrategy filterCacheStrategy) {
+      _filterCacheStrategy = filterCacheStrategy;
+
+      if (readAuthorizations == null || readAuthorizations.isEmpty()) {
+        _noReadAccess = true;
+        _readDocumentVisibilityFilter = null;
+      } else {
+        _readDocumentVisibilityFilter = new DocumentVisibilityFilter(READ_FIELD, new DocumentAuthorizations(
+            readAuthorizations), _filterCacheStrategy);
+      }
+
+      if (discoverAuthorizations == null || discoverAuthorizations.isEmpty()) {
+        _noDiscoverAccess = true;
+        _discoverDocumentVisibilityFilter = null;
+      } else {
+        _discoverDocumentVisibilityFilter = new DocumentVisibilityFilter(DISCOVER_FIELD, new DocumentAuthorizations(
+            discoverAuthorizations), _filterCacheStrategy);
+      }
+      _discoverableFields = discoverableFields;
+    }
+
+    @Override
+    protected boolean readAccess(int docID) throws IOException {
+      checkClone();
+      if (_noReadAccess) {
+        return false;
+      }
+      return _readBits.get(docID);
+    }
+
+    private void checkClone() throws IOException {
+      if (!_isClone) {
+        throw new IOException("No AtomicReader set.");
+      }
+    }
+
+    @Override
+    protected boolean discoverAccess(int docID) throws IOException {
+      checkClone();
+      if (_noDiscoverAccess) {
+        return false;
+      }
+      return _discoverBits.get(docID);
+    }
+
+    @Override
+    protected boolean readOrDiscoverAccess(int docID) throws IOException {
+      if (readAccess(docID)) {
+        return true;
+      } else {
+        return discoverAccess(docID);
+      }
+    }
+
+    @Override
+    public boolean canDiscoverField(String name) {
+      return _discoverableFields.contains(name);
+    }
+
+    @Override
+    public AccessControlReader clone(AtomicReader in) throws IOException {
+      try {
+        FilterAccessControlReader filterAccessControlReader = (FilterAccessControlReader) super.clone();
+        filterAccessControlReader._isClone = true;
+        if (_readDocumentVisibilityFilter == null) {
+          filterAccessControlReader._noReadAccess = true;
+        } else {
+          DocIdSet readDocIdSet = _readDocumentVisibilityFilter.getDocIdSet(in.getContext(), in.getLiveDocs());
+          if (readDocIdSet == DocIdSet.EMPTY_DOCIDSET || readDocIdSet == null) {
+            filterAccessControlReader._noReadAccess = true;
+          } else {
+            filterAccessControlReader._readBits = readDocIdSet.bits();
+            if (filterAccessControlReader._readBits == null) {
+              throw new IOException("Read Bits can not be null.");
+            }
+          }
+          filterAccessControlReader._readDocIdSet = readDocIdSet;
+        }
+
+        if (_discoverDocumentVisibilityFilter == null) {
+          filterAccessControlReader._noDiscoverAccess = true;
+        } else {
+          DocIdSet discoverDocIdSet = _discoverDocumentVisibilityFilter.getDocIdSet(in.getContext(), in.getLiveDocs());
+          if (discoverDocIdSet == DocIdSet.EMPTY_DOCIDSET || discoverDocIdSet == null) {
+            filterAccessControlReader._noDiscoverAccess = true;
+          } else {
+            filterAccessControlReader._discoverBits = discoverDocIdSet.bits();
+            if (filterAccessControlReader._discoverBits == null) {
+              throw new IOException("Read Bits can not be null.");
+            }
+          }
+          filterAccessControlReader._discoverDocIdSet = discoverDocIdSet;
+        }
+        return filterAccessControlReader;
+      } catch (CloneNotSupportedException e) {
+        throw new IOException(e);
+      }
+    }
+
+    @Override
+    public Filter getQueryFilter() throws IOException {
+      return new Filter() {
+        @Override
+        public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException {
+          FilterAccessControlReader accessControlReader = (FilterAccessControlReader) FilterAccessControlReader.this
+              .clone(context.reader());
+          DocIdSet secureDocIdSet = getSecureDocIdSet(accessControlReader);
+          if (acceptDocs == null) {
+            return secureDocIdSet;
+          } else {
+            return applyDeletes(acceptDocs, secureDocIdSet);
+          }
+        }
+
+        private DocIdSet getSecureDocIdSet(FilterAccessControlReader accessControlReader) throws IOException {
+          DocIdSet readDocIdSet = accessControlReader._readDocIdSet;
+          DocIdSet discoverDocIdSet = accessControlReader._discoverDocIdSet;
+          if (isEmptyOrNull(discoverDocIdSet) && isEmptyOrNull(readDocIdSet)) {
+            return DocIdSet.EMPTY_DOCIDSET;
+          } else if (isEmptyOrNull(discoverDocIdSet)) {
+            return readDocIdSet;
+          } else if (isEmptyOrNull(readDocIdSet)) {
+            return discoverDocIdSet;
+          } else {
+            return DocumentVisibilityFilter.getLogicalOr(readDocIdSet, discoverDocIdSet);
+          }
+        }
+
+        private boolean isEmptyOrNull(DocIdSet docIdSet) {
+          if (docIdSet == null || docIdSet == DocIdSet.EMPTY_DOCIDSET) {
+            return true;
+          }
+          return false;
+        }
+      };
+    }
+
+    protected DocIdSet applyDeletes(final Bits acceptDocs, final DocIdSet secureDocIdSet) {
+      return new DocIdSet() {
+
+        @Override
+        public DocIdSetIterator iterator() throws IOException {
+          final DocIdSetIterator docIdSetIterator = secureDocIdSet.iterator();
+          return new DocIdSetIterator() {
+
+            @Override
+            public int nextDoc() throws IOException {
+              int docId;
+              while ((docId = docIdSetIterator.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
+                if (acceptDocs.get(docId)) {
+                  return docId;
+                }
+              }
+              return DocIdSetIterator.NO_MORE_DOCS;
+            }
+
+            @Override
+            public int advance(int target) throws IOException {
+              int docId = docIdSetIterator.advance(target);
+              if (docId == DocIdSetIterator.NO_MORE_DOCS) {
+                return DocIdSetIterator.NO_MORE_DOCS;
+              }
+              if (acceptDocs.get(docId)) {
+                return docId;
+              }
+              while ((docId = docIdSetIterator.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
+                if (acceptDocs.get(docId)) {
+                  return docId;
+                }
+              }
+              return DocIdSetIterator.NO_MORE_DOCS;
+            }
+
+            @Override
+            public int docID() {
+              return docIdSetIterator.docID();
+            }
+
+            @Override
+            public long cost() {
+              return docIdSetIterator.cost() + 1;
+            }
+
+          };
+        }
+      };
+    }
+  }
+
+  public static class FilterAccessControlWriter extends AccessControlWriter {
+
+    @Override
+    public Iterable<IndexableField> addReadVisiblity(String read, Iterable<IndexableField> fields) {
+      return addField(fields, new DocumentVisiblityField(READ_FIELD, read, Store.YES));
+    }
+
+    @Override
+    public Iterable<IndexableField> addDiscoverVisiblity(String discover, Iterable<IndexableField> fields) {
+      return addField(fields, new DocumentVisiblityField(DISCOVER_FIELD, discover, Store.YES));
+    }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/ReadType.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/ReadType.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/ReadType.java
new file mode 100644
index 0000000..dec08b0
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/ReadType.java
@@ -0,0 +1,21 @@
+/**
+ * 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.index;
+
+public enum ReadType {
+  LIVEDOCS, DOCUMENT_FETCH_READ, DOCUMENT_FETCH_DISCOVER, NUMERIC_DOC_VALUE, BINARY_DOC_VALUE, SORTED_DOC_VALUE, NORM_VALUE, SORTED_SET_DOC_VALUE, DOCS_ENUM, QUERY
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/SecureAtomicReader.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/SecureAtomicReader.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/SecureAtomicReader.java
new file mode 100644
index 0000000..9155769
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/SecureAtomicReader.java
@@ -0,0 +1,416 @@
+/**
+ * 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.index;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Set;
+
+import org.apache.lucene.index.AtomicReader;
+import org.apache.lucene.index.BinaryDocValues;
+import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.Fields;
+import org.apache.lucene.index.FilterAtomicReader;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.index.StoredFieldVisitor;
+import org.apache.lucene.index.Terms;
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.automaton.CompiledAutomaton;
+
+/**
+ * The current {@link SecureAtomicReader} will protect access to documents based
+ * on the {@link AccessControl} object.
+ * 
+ * NOTE: If you are using the {@link Fields} and {@link Terms} with
+ * {@link TermsEnum} to create a type ahead. Make sure that you check that the
+ * {@link TermsEnum} actually points to a single document because the
+ * {@link SecureAtomicReader} will leak terms that users don't have access to
+ * read or discover.
+ */
+public class SecureAtomicReader extends FilterAtomicReader {
+
+  private final AccessControlReader _accessControl;
+  private final AtomicReader _original;
+
+  public static SecureAtomicReader create(AccessControlFactory accessControlFactory, AtomicReader in,
+      Collection<String> readAuthorizations, Collection<String> discoverAuthorizations, Set<String> discoverableFields)
+      throws IOException {
+    AccessControlReader accessControlReader = accessControlFactory.getReader(readAuthorizations,
+        discoverAuthorizations, discoverableFields);
+    return new SecureAtomicReader(in, accessControlReader);
+  }
+
+  public SecureAtomicReader(AtomicReader in, AccessControlReader accessControlReader) throws IOException {
+    super(in);
+    _accessControl = accessControlReader.clone(in);
+    _original = in;
+  }
+
+  public AtomicReader getOriginalReader() {
+    return _original;
+  }
+
+  @Override
+  public Bits getLiveDocs() {
+    final Bits liveDocs = in.getLiveDocs();
+    final int maxDoc = maxDoc();
+    return new Bits() {
+
+      @Override
+      public boolean get(int index) {
+        if (liveDocs == null || liveDocs.get(index)) {
+          // Need to check access
+          try {
+            if (_accessControl.hasAccess(ReadType.LIVEDOCS, index)) {
+              return true;
+            }
+          } catch (IOException e) {
+            throw new RuntimeException(e);
+          }
+        }
+        return false;
+      }
+
+      @Override
+      public int length() {
+        return maxDoc;
+      }
+
+    };
+  }
+
+  @Override
+  public Fields getTermVectors(int docID) throws IOException {
+    // use doc auth
+    throw new RuntimeException("Not implemented.");
+  }
+
+  @Override
+  public void document(int docID, final StoredFieldVisitor visitor) throws IOException {
+    if (_accessControl.hasAccess(ReadType.DOCUMENT_FETCH_READ, docID)) {
+      in.document(docID, visitor);
+      return;
+    }
+    if (_accessControl.hasAccess(ReadType.DOCUMENT_FETCH_DISCOVER, docID)) {
+      // TODO add way to perform code when visitor runs here....
+      in.document(docID, new StoredFieldVisitor() {
+        @Override
+        public Status needsField(FieldInfo fieldInfo) throws IOException {
+          if (_accessControl.canDiscoverField(fieldInfo.name)) {
+            return visitor.needsField(fieldInfo);
+          } else {
+            return Status.NO;
+          }
+        }
+
+        @Override
+        public void binaryField(FieldInfo fieldInfo, byte[] value) throws IOException {
+          visitor.binaryField(fieldInfo, value);
+        }
+
+        @Override
+        public void stringField(FieldInfo fieldInfo, String value) throws IOException {
+          visitor.stringField(fieldInfo, value);
+        }
+
+        @Override
+        public void intField(FieldInfo fieldInfo, int value) throws IOException {
+          visitor.intField(fieldInfo, value);
+        }
+
+        @Override
+        public void longField(FieldInfo fieldInfo, long value) throws IOException {
+          visitor.longField(fieldInfo, value);
+        }
+
+        @Override
+        public void floatField(FieldInfo fieldInfo, float value) throws IOException {
+          visitor.floatField(fieldInfo, value);
+        }
+
+        @Override
+        public void doubleField(FieldInfo fieldInfo, double value) throws IOException {
+          visitor.doubleField(fieldInfo, value);
+        }
+
+      });
+      return;
+    }
+  }
+
+  @Override
+  public Fields fields() throws IOException {
+    return new SecureFields(in.fields(), _accessControl, maxDoc());
+  }
+
+  @Override
+  public NumericDocValues getNumericDocValues(String field) throws IOException {
+    return secureNumericDocValues(in.getNumericDocValues(field), ReadType.NUMERIC_DOC_VALUE);
+  }
+
+  private NumericDocValues secureNumericDocValues(final NumericDocValues numericDocValues, final ReadType type) {
+    if (numericDocValues == null) {
+      return null;
+    }
+    return new NumericDocValues() {
+
+      @Override
+      public long get(int docID) {
+        try {
+          if (_accessControl.hasAccess(type, docID)) {
+            return numericDocValues.get(docID);
+          }
+          return 0L; // Default missing value.
+        } catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+      }
+    };
+  }
+
+  @Override
+  public BinaryDocValues getBinaryDocValues(String field) throws IOException {
+    final BinaryDocValues binaryDocValues = in.getBinaryDocValues(field);
+    if (binaryDocValues == null) {
+      return null;
+    }
+    return new BinaryDocValues() {
+
+      @Override
+      public void get(int docID, BytesRef result) {
+        try {
+          if (_accessControl.hasAccess(ReadType.BINARY_DOC_VALUE, docID)) {
+            binaryDocValues.get(docID, result);
+            return;
+          }
+          // Default missing value.
+          result.bytes = MISSING;
+          result.length = 0;
+          result.offset = 0;
+        } catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+      }
+    };
+  }
+
+  @Override
+  public SortedDocValues getSortedDocValues(String field) throws IOException {
+    final SortedDocValues sortedDocValues = in.getSortedDocValues(field);
+    if (sortedDocValues == null) {
+      return null;
+    }
+    return new SortedDocValues() {
+
+      @Override
+      public void lookupOrd(int ord, BytesRef result) {
+        sortedDocValues.lookupOrd(ord, result);
+      }
+
+      @Override
+      public int getValueCount() {
+        return sortedDocValues.getValueCount();
+      }
+
+      @Override
+      public int getOrd(int docID) {
+        try {
+          if (_accessControl.hasAccess(ReadType.SORTED_DOC_VALUE, docID)) {
+            return sortedDocValues.getOrd(docID);
+          }
+          return -1; // Default missing value.
+        } catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+      }
+    };
+  }
+
+  @Override
+  public SortedSetDocValues getSortedSetDocValues(String field) throws IOException {
+    final SortedSetDocValues sortedSetDocValues = in.getSortedSetDocValues(field);
+    if (sortedSetDocValues == null) {
+      return null;
+    }
+    return new SortedSetDocValues() {
+
+      private boolean _access;
+
+      @Override
+      public void setDocument(int docID) {
+        try {
+          if (_access = _accessControl.hasAccess(ReadType.SORTED_SET_DOC_VALUE, docID)) {
+            sortedSetDocValues.setDocument(docID);
+          }
+        } catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+      }
+
+      @Override
+      public long nextOrd() {
+        if (_access) {
+          return sortedSetDocValues.nextOrd();
+        }
+        return NO_MORE_ORDS;
+      }
+
+      @Override
+      public void lookupOrd(long ord, BytesRef result) {
+        if (_access) {
+          sortedSetDocValues.lookupOrd(ord, result);
+        } else {
+          result.bytes = BinaryDocValues.MISSING;
+          result.length = 0;
+          result.offset = 0;
+        }
+      }
+
+      @Override
+      public long getValueCount() {
+        return sortedSetDocValues.getValueCount();
+      }
+    };
+  }
+
+  @Override
+  public NumericDocValues getNormValues(String field) throws IOException {
+    return secureNumericDocValues(in.getNormValues(field), ReadType.NORM_VALUE);
+  }
+
+  static class SecureFields extends FilterFields {
+
+    private final int _maxDoc;
+    private final AccessControlReader _accessControlReader;
+
+    public SecureFields(Fields in, AccessControlReader accessControlReader, int maxDoc) {
+      super(in);
+      _accessControlReader = accessControlReader;
+      _maxDoc = maxDoc;
+    }
+
+    @Override
+    public Terms terms(String field) throws IOException {
+      Terms terms = in.terms(field);
+      if (terms == null) {
+        return null;
+      }
+      return new SecureTerms(terms, _accessControlReader, _maxDoc);
+    }
+
+  }
+
+  static class SecureTerms extends FilterTerms {
+
+    private final int _maxDoc;
+    private final AccessControlReader _accessControlReader;
+
+    public SecureTerms(Terms in, AccessControlReader accessControlReader, int maxDoc) {
+      super(in);
+      _accessControlReader = accessControlReader;
+      _maxDoc = maxDoc;
+    }
+
+    @Override
+    public TermsEnum iterator(TermsEnum reuse) throws IOException {
+      return new SecureTermsEnum(in.iterator(reuse), _accessControlReader, _maxDoc);
+    }
+
+    @Override
+    public TermsEnum intersect(CompiledAutomaton compiled, BytesRef startTerm) throws IOException {
+      return new SecureTermsEnum(in.intersect(compiled, startTerm), _accessControlReader, _maxDoc);
+    }
+  }
+
+  static class SecureTermsEnum extends FilterTermsEnum {
+
+    private final int _maxDoc;
+    private final AccessControlReader _accessControlReader;
+
+    public SecureTermsEnum(TermsEnum in, AccessControlReader accessControlReader, int maxDoc) {
+      super(in);
+      _accessControlReader = accessControlReader;
+      _maxDoc = maxDoc;
+    }
+
+    @Override
+    public DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) throws IOException {
+      Bits secureLiveDocs = getSecureLiveDocs(liveDocs, _maxDoc, _accessControlReader);
+      return in.docs(secureLiveDocs, reuse, flags);
+    }
+
+    @Override
+    public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags)
+        throws IOException {
+      Bits secureLiveDocs = getSecureLiveDocs(liveDocs, _maxDoc, _accessControlReader);
+      return in.docsAndPositions(secureLiveDocs, reuse, flags);
+    }
+
+  }
+
+  public static Bits getSecureLiveDocs(Bits bits, int maxDoc, final AccessControlReader accessControlReader) {
+    final Bits liveDocs;
+    if (bits == null) {
+      liveDocs = getMatchAll(maxDoc);
+    } else {
+      liveDocs = bits;
+    }
+    final int length = liveDocs.length();
+    Bits secureLiveDocs = new Bits() {
+      @Override
+      public boolean get(int index) {
+        if (liveDocs.get(index)) {
+          try {
+            if (accessControlReader.hasAccess(ReadType.DOCS_ENUM, index)) {
+              return true;
+            }
+          } catch (IOException e) {
+            throw new RuntimeException(e);
+          }
+        }
+        return false;
+      }
+
+      @Override
+      public int length() {
+        return length;
+      }
+    };
+    return secureLiveDocs;
+  }
+
+  public static Bits getMatchAll(final int length) {
+    return new Bits() {
+
+      @Override
+      public int length() {
+        return length;
+      }
+
+      @Override
+      public boolean get(int index) {
+        return true;
+      }
+    };
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/SecureDirectoryReader.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/SecureDirectoryReader.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/SecureDirectoryReader.java
new file mode 100644
index 0000000..55cffbf
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/index/SecureDirectoryReader.java
@@ -0,0 +1,66 @@
+/**
+ * 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.index;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Set;
+
+import org.apache.lucene.index.AtomicReader;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.FilterDirectoryReader;
+
+public class SecureDirectoryReader extends FilterDirectoryReader {
+
+  public static SecureDirectoryReader create(AccessControlFactory accessControlFactory, DirectoryReader in,
+      Collection<String> readAuthorizations, Collection<String> discoverAuthorizations, Set<String> discoverableFields)
+      throws IOException {
+    AccessControlReader accessControlReader = accessControlFactory.getReader(readAuthorizations,
+        discoverAuthorizations, discoverableFields);
+    return new SecureDirectoryReader(in, accessControlReader);
+  }
+
+  private final DirectoryReader _original;
+
+  public SecureDirectoryReader(DirectoryReader in, final AccessControlReader accessControlReader) {
+    super(in, new SubReaderWrapper() {
+
+      @Override
+      public AtomicReader wrap(AtomicReader reader) {
+        try {
+          return new SecureAtomicReader(reader, accessControlReader);
+        } catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+      }
+    });
+    _original = in;
+  }
+
+  public DirectoryReader getOriginal() {
+    return _original;
+  }
+
+  @Override
+  protected DirectoryReader doWrapDirectoryReader(DirectoryReader in) {
+    if (in instanceof SecureDirectoryReader) {
+      return in;
+    }
+    throw new RuntimeException("Not allowed.");
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/BitSetDocumentVisibilityFilterCacheStrategy.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/BitSetDocumentVisibilityFilterCacheStrategy.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/BitSetDocumentVisibilityFilterCacheStrategy.java
new file mode 100644
index 0000000..0a387f6
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/BitSetDocumentVisibilityFilterCacheStrategy.java
@@ -0,0 +1,152 @@
+/**
+ * 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.concurrent.ConcurrentMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.lucene.index.AtomicReader;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexReader.ReaderClosedListener;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.OpenBitSet;
+
+import com.google.common.collect.MapMaker;
+
+public class BitSetDocumentVisibilityFilterCacheStrategy extends DocumentVisibilityFilterCacheStrategy {
+
+  private static final Log LOG = LogFactory.getLog(BitSetDocumentVisibilityFilterCacheStrategy.class);
+
+  public static final DocumentVisibilityFilterCacheStrategy INSTANCE = new BitSetDocumentVisibilityFilterCacheStrategy();
+
+  private final ConcurrentMap<Key, DocIdSet> _cache;
+
+  public BitSetDocumentVisibilityFilterCacheStrategy() {
+    _cache = new MapMaker().makeMap();
+  }
+
+  @Override
+  public DocIdSet getDocIdSet(String fieldName, BytesRef term, AtomicReader reader) {
+    Key key = new Key(fieldName, term, reader.getCoreCacheKey());
+    DocIdSet docIdSet = _cache.get(key);
+    if (docIdSet != null) {
+      LOG.debug("Cache hit for key [" + key + "]");
+    } else {
+      LOG.debug("Cache miss for key [" + key + "]");
+    }
+    return docIdSet;
+  }
+
+  @Override
+  public Builder createBuilder(String fieldName, BytesRef term, final AtomicReader reader) {
+    final OpenBitSet bitSet = new OpenBitSet(reader.maxDoc());
+    final Key key = new Key(fieldName, term, reader.getCoreCacheKey());
+    LOG.debug("Creating new bitset for key [" + key + "] on index [" + reader + "]");
+    return new Builder() {
+      @Override
+      public void or(DocIdSetIterator it) throws IOException {
+        int doc;
+        while ((doc = it.nextDoc()) != DocsEnum.NO_MORE_DOCS) {
+          bitSet.set(doc);
+        }
+      }
+
+      @Override
+      public DocIdSet getDocIdSet() throws IOException {
+        reader.addReaderClosedListener(new ReaderClosedListener() {
+          @Override
+          public void onClose(IndexReader reader) {
+            LOG.debug("Removing old bitset for key [" + key + "]");
+            DocIdSet docIdSet = _cache.remove(key);
+            if (docIdSet == null) {
+              LOG.warn("DocIdSet was missing for key [" + docIdSet + "]");
+            }
+          }
+        });
+        _cache.put(key, bitSet);
+        return bitSet;
+      }
+    };
+  }
+
+  private static class Key {
+
+    private final Object _object;
+    private final BytesRef _term;
+    private final String _fieldName;
+
+    public Key(String fieldName, BytesRef term, Object object) {
+      _fieldName = fieldName;
+      _term = BytesRef.deepCopyOf(term);
+      _object = object;
+    }
+
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((_fieldName == null) ? 0 : _fieldName.hashCode());
+      result = prime * result + ((_object == null) ? 0 : _object.hashCode());
+      result = prime * result + ((_term == null) ? 0 : _term.hashCode());
+      return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj)
+        return true;
+      if (obj == null)
+        return false;
+      if (getClass() != obj.getClass())
+        return false;
+      Key other = (Key) obj;
+      if (_fieldName == null) {
+        if (other._fieldName != null)
+          return false;
+      } else if (!_fieldName.equals(other._fieldName))
+        return false;
+      if (_object == null) {
+        if (other._object != null)
+          return false;
+      } else if (!_object.equals(other._object))
+        return false;
+      if (_term == null) {
+        if (other._term != null)
+          return false;
+      } else if (!_term.equals(other._term))
+        return false;
+      return true;
+    }
+
+    @Override
+    public String toString() {
+      return "Key [_object=" + _object + ", _fieldName=" + _fieldName + ", _term=" + _term + "]";
+    }
+
+  }
+
+  @Override
+  public String toString() {
+    return "BitSetDocumentVisibilityFilterCacheStrategy [_cache=" + _cache + "]";
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/DocumentVisibilityFilter.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/DocumentVisibilityFilter.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/DocumentVisibilityFilter.java
new file mode 100644
index 0000000..8a77421
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/DocumentVisibilityFilter.java
@@ -0,0 +1,232 @@
+/**
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.blur.lucene.security.DocumentAuthorizations;
+import org.apache.blur.lucene.security.DocumentVisibility;
+import org.apache.blur.lucene.security.DocumentVisibilityEvaluator;
+import org.apache.blur.lucene.security.search.DocumentVisibilityFilterCacheStrategy.Builder;
+import org.apache.lucene.index.AtomicReader;
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.Fields;
+import org.apache.lucene.index.Terms;
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
+
+public class DocumentVisibilityFilter extends Filter {
+
+  private static final Comparator<DocIdSetIterator> COMPARATOR = new Comparator<DocIdSetIterator>() {
+    @Override
+    public int compare(DocIdSetIterator o1, DocIdSetIterator o2) {
+      int docID1 = o1.docID();
+      int docID2 = o2.docID();
+      return docID1 - docID2;
+    }
+  };
+
+  private final String _fieldName;
+  private final DocumentAuthorizations _authorizations;
+  private final DocumentVisibilityFilterCacheStrategy _filterCacheStrategy;
+
+  public DocumentVisibilityFilter(String fieldName, DocumentAuthorizations authorizations,
+      DocumentVisibilityFilterCacheStrategy filterCacheStrategy) {
+    _fieldName = fieldName;
+    _authorizations = authorizations;
+    _filterCacheStrategy = filterCacheStrategy;
+  }
+
+  @Override
+  public String toString() {
+    return "DocumentVisibilityFilter [_fieldName=" + _fieldName + ", _authorizations=" + _authorizations
+        + ", _filterCacheStrategy=" + _filterCacheStrategy + "]";
+  }
+
+  @Override
+  public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException {
+    AtomicReader reader = context.reader();
+    List<DocIdSet> list = new ArrayList<DocIdSet>();
+
+    Fields fields = reader.fields();
+    Terms terms = fields.terms(_fieldName);
+    if (terms == null) {
+      // if field is not present then show nothing.
+      return DocIdSet.EMPTY_DOCIDSET;
+    }
+    TermsEnum iterator = terms.iterator(null);
+    BytesRef bytesRef;
+    DocumentVisibilityEvaluator visibilityEvaluator = new DocumentVisibilityEvaluator(_authorizations);
+    while ((bytesRef = iterator.next()) != null) {
+      if (isVisible(visibilityEvaluator, bytesRef)) {
+        DocIdSet docIdSet = _filterCacheStrategy.getDocIdSet(_fieldName, bytesRef, reader);
+        if (docIdSet != null) {
+          list.add(docIdSet);
+        } else {
+          // Do not use acceptDocs because we want the acl cache to be version
+          // agnostic.
+          DocsEnum docsEnum = iterator.docs(null, null);
+          list.add(buildCache(reader, docsEnum, bytesRef));
+        }
+      }
+    }
+    return getLogicalOr(list);
+  }
+
+  private DocIdSet buildCache(AtomicReader reader, DocIdSetIterator it, BytesRef bytesRef) throws IOException {
+    Builder builder = _filterCacheStrategy.createBuilder(_fieldName, bytesRef, reader);
+    builder.or(it);
+    return builder.getDocIdSet();
+  }
+
+  private boolean isVisible(DocumentVisibilityEvaluator visibilityEvaluator, BytesRef bytesRef) throws IOException {
+    DocumentVisibility visibility = new DocumentVisibility(trim(bytesRef));
+    return visibilityEvaluator.evaluate(visibility);
+  }
+
+  private byte[] trim(BytesRef bytesRef) {
+    byte[] buf = new byte[bytesRef.length];
+    System.arraycopy(bytesRef.bytes, bytesRef.offset, buf, 0, bytesRef.length);
+    return buf;
+  }
+
+  public static DocIdSet getLogicalOr(DocIdSet... list) throws IOException {
+    return getLogicalOr(Arrays.asList(list));
+  }
+
+  public static DocIdSet getLogicalOr(final List<DocIdSet> list) throws IOException {
+    if (list.size() == 0) {
+      return DocIdSet.EMPTY_DOCIDSET;
+    }
+    if (list.size() == 1) {
+      DocIdSet docIdSet = list.get(0);
+      Bits bits = docIdSet.bits();
+      if (bits == null) {
+        throw new IOException("Bits are not allowed to be null for DocIdSet [" + docIdSet + "].");
+      }
+      return docIdSet;
+    }
+    int index = 0;
+    final Bits[] bitsArray = new Bits[list.size()];
+    int length = -1;
+    for (DocIdSet docIdSet : list) {
+      Bits bits = docIdSet.bits();
+      if (bits == null) {
+        throw new IOException("Bits are not allowed to be null for DocIdSet [" + docIdSet + "].");
+      }
+      bitsArray[index] = bits;
+      index++;
+      if (length < 0) {
+        length = bits.length();
+      } else if (length != bits.length()) {
+        throw new IOException("Bits length need to be the same [" + length + "] and [" + bits.length() + "]");
+      }
+    }
+    final int len = length;
+    return new DocIdSet() {
+
+      @Override
+      public Bits bits() throws IOException {
+        return new Bits() {
+
+          @Override
+          public boolean get(int index) {
+            for (int i = 0; i < bitsArray.length; i++) {
+              if (bitsArray[i].get(index)) {
+                return true;
+              }
+            }
+            return false;
+          }
+
+          @Override
+          public int length() {
+            return len;
+          }
+
+        };
+      }
+
+      @Override
+      public boolean isCacheable() {
+        return true;
+      }
+
+      @Override
+      public DocIdSetIterator iterator() throws IOException {
+        final DocIdSetIterator[] docIdSetIteratorArray = new DocIdSetIterator[list.size()];
+        long c = 0;
+        int index = 0;
+        for (DocIdSet docIdSet : list) {
+          DocIdSetIterator iterator = docIdSet.iterator();
+          iterator.nextDoc();
+          docIdSetIteratorArray[index] = iterator;
+          c += iterator.cost();
+          index++;
+        }
+        final long cost = c;
+        return new DocIdSetIterator() {
+
+          private int _docId = -1;
+
+          @Override
+          public int advance(int target) throws IOException {
+            callAdvanceOnAllThatAreBehind(target);
+            Arrays.sort(docIdSetIteratorArray, COMPARATOR);
+            DocIdSetIterator iterator = docIdSetIteratorArray[0];
+            return _docId = iterator.docID();
+          }
+
+          private void callAdvanceOnAllThatAreBehind(int target) throws IOException {
+            for (int i = 0; i < docIdSetIteratorArray.length; i++) {
+              DocIdSetIterator iterator = docIdSetIteratorArray[i];
+              if (iterator.docID() < target) {
+                iterator.advance(target);
+              }
+            }
+          }
+
+          @Override
+          public int nextDoc() throws IOException {
+            return advance(_docId + 1);
+          }
+
+          @Override
+          public int docID() {
+            return _docId;
+          }
+
+          @Override
+          public long cost() {
+            return cost;
+          }
+
+        };
+      }
+    };
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/DocumentVisibilityFilterCacheStrategy.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/DocumentVisibilityFilterCacheStrategy.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/DocumentVisibilityFilterCacheStrategy.java
new file mode 100644
index 0000000..17d1995
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/DocumentVisibilityFilterCacheStrategy.java
@@ -0,0 +1,39 @@
+/**
+ * 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 org.apache.lucene.index.AtomicReader;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.util.BytesRef;
+
+public abstract class DocumentVisibilityFilterCacheStrategy {
+
+  public abstract DocIdSet getDocIdSet(String fieldName, BytesRef term, AtomicReader reader) throws IOException;
+
+  public abstract Builder createBuilder(String fieldName, BytesRef term, AtomicReader reader) throws IOException;
+
+  public abstract static class Builder {
+
+    public abstract void or(DocIdSetIterator it) throws IOException;
+
+    public abstract DocIdSet getDocIdSet() throws IOException;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/SecureIndexSearcher.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/SecureIndexSearcher.java b/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/SecureIndexSearcher.java
new file mode 100644
index 0000000..5714278
--- /dev/null
+++ b/blur-document-security/src/main/java/org/apache/blur/lucene/security/search/SecureIndexSearcher.java
@@ -0,0 +1,170 @@
+/**
+ * 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 AccessControlReader _accessControlReader;
+
+  public SecureIndexSearcher(IndexReader r, AccessControlFactory accessControlFactory,
+      Collection<String> readAuthorizations, Collection<String> discoverAuthorizations, Set<String> discoverableFields)
+      throws IOException {
+    this(r, null, accessControlFactory, readAuthorizations, discoverAuthorizations, discoverableFields);
+  }
+
+  public SecureIndexSearcher(IndexReader r, ExecutorService executor, AccessControlFactory accessControlFactory,
+      Collection<String> readAuthorizations, Collection<String> discoverAuthorizations, Set<String> discoverableFields)
+      throws IOException {
+    this(r.getContext(), executor, accessControlFactory, readAuthorizations, discoverAuthorizations, discoverableFields);
+  }
+
+  public SecureIndexSearcher(IndexReaderContext context, AccessControlFactory accessControlFactory,
+      Collection<String> readAuthorizations, Collection<String> discoverAuthorizations, Set<String> discoverableFields)
+      throws IOException {
+    this(context, null, accessControlFactory, readAuthorizations, discoverAuthorizations, discoverableFields);
+  }
+
+  public SecureIndexSearcher(IndexReaderContext context, ExecutorService executor,
+      AccessControlFactory accessControlFactory, Collection<String> readAuthorizations,
+      Collection<String> discoverAuthorizations, Set<String> discoverableFields) throws IOException {
+    super(context, executor);
+    _accessControlFactory = accessControlFactory;
+    _readAuthorizations = readAuthorizations;
+    _discoverAuthorizations = discoverAuthorizations;
+    _discoverableFields = discoverableFields;
+    _accessControlReader = _accessControlFactory.getReader(readAuthorizations, discoverAuthorizations,
+        discoverableFields);
+    _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);
+  }
+
+  protected IndexReader getSecureIndexReader(IndexReaderContext context) throws IOException {
+    IndexReader indexReader = context.reader();
+    if (indexReader instanceof DirectoryReader) {
+      return SecureDirectoryReader.create(_accessControlFactory, (DirectoryReader) indexReader, _readAuthorizations,
+          _discoverAuthorizations, _discoverableFields);
+    } else if (indexReader instanceof AtomicReader) {
+      return SecureAtomicReader.create(_accessControlFactory, (AtomicReader) indexReader, _readAuthorizations,
+          _discoverAuthorizations, _discoverableFields);
+    }
+    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);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/test/java/org/apache/blur/lucene/security/IndexSearcherTest.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/test/java/org/apache/blur/lucene/security/IndexSearcherTest.java b/blur-document-security/src/test/java/org/apache/blur/lucene/security/IndexSearcherTest.java
new file mode 100644
index 0000000..55ad9f5
--- /dev/null
+++ b/blur-document-security/src/test/java/org/apache/blur/lucene/security/IndexSearcherTest.java
@@ -0,0 +1,235 @@
+/**
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.blur.lucene.security.DocumentAuthorizations;
+import org.apache.blur.lucene.security.DocumentVisibility;
+import org.apache.blur.lucene.security.DocumentVisibilityEvaluator;
+import org.apache.blur.lucene.security.index.AccessControlFactory;
+import org.apache.blur.lucene.security.index.AccessControlWriter;
+import org.apache.blur.lucene.security.index.FilterAccessControlFactory;
+import org.apache.blur.lucene.security.index.SecureAtomicReader;
+import org.apache.blur.lucene.security.search.SecureIndexSearcher;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.util.Version;
+import org.junit.Test;
+
+public class IndexSearcherTest {
+
+  private static final Collection<String> EMPTY = Arrays.asList(new String[] {});
+  private AccessControlFactory _accessControlFactory = new FilterAccessControlFactory();
+
+  @Test
+  public void test1() throws ParseException, IOException {
+    runTest(1, list("d", "a", "b"));
+  }
+
+  @Test
+  public void test2() throws ParseException, IOException {
+    runTest(2, list("c", "a", "b"));
+  }
+
+  @Test
+  public void test3() throws ParseException, IOException {
+    runTest(0, list("x"));
+  }
+
+  @Test
+  public void test4() throws ParseException, IOException {
+    runTest(3, list("c", "a", "b"), list("c", "a", "b"), list("_read_", "_discover_"));
+  }
+
+  @Test
+  public void test5() throws ParseException, IOException {
+    runTest(3, list("c", "a", "b"), list("c", "a", "b"), list("_read_", "_discover_"));
+  }
+
+  private void runTest(int expected, Collection<String> readAuthorizations) throws IOException, ParseException {
+    runTest(expected, readAuthorizations, EMPTY, EMPTY);
+  }
+
+  private void runTest(int expected, Collection<String> readAuthorizations, Collection<String> discoverAuthorizations,
+      Collection<String> discoverableFields) throws IOException, ParseException {
+    IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_43, new StandardAnalyzer(Version.LUCENE_43));
+    Directory dir = new RAMDirectory();
+    {
+      IndexWriter writer = new IndexWriter(dir, conf);
+      writer.addDocument(getEmpty());
+      writer.commit();
+      writer.addDocument(getDoc(0, "(a&b)|d", null, "f1", "f2"));
+      writer.addDocument(getDoc(1, "a&b&c", null, "f1", "f2"));
+      writer.addDocument(getDoc(2, "a&b&c&e", "a&b&c", "f1", "f2"));
+      writer.addDocument(getDoc(3, null, null, "f1", "f2"));// can't find
+      writer.close(false);
+    }
+    DirectoryReader reader = DirectoryReader.open(dir);
+    validate(expected, 2, readAuthorizations, discoverAuthorizations, discoverableFields, dir, reader);
+    {
+      IndexWriter writer = new IndexWriter(dir, conf);
+      writer.deleteDocuments(new Term("id", "0"));
+      writer.addDocument(getDoc(0, "(a&b)|d", null, "f1", "f2"));
+      writer.close(false);
+    }
+    reader = DirectoryReader.openIfChanged(reader);
+    validate(expected, 3, readAuthorizations, discoverAuthorizations, discoverableFields, dir, reader);
+    {
+      IndexWriter writer = new IndexWriter(dir, conf);
+      writer.deleteDocuments(new Term("id", "1"));
+      writer.addDocument(getDoc(1, "a&b&c", null, "f1", "f2"));
+      writer.close(false);
+    }
+    reader = DirectoryReader.openIfChanged(reader);
+    validate(expected, 4, readAuthorizations, discoverAuthorizations, discoverableFields, dir, reader);
+  }
+
+  private void validate(int expected, int leafCount, Collection<String> readAuthorizations,
+      Collection<String> discoverAuthorizations, Collection<String> discoverableFields, Directory dir,
+      IndexReader reader) throws IOException {
+    List<AtomicReaderContext> leaves = reader.leaves();
+    assertEquals(leafCount, leaves.size());
+    SecureIndexSearcher searcher = new SecureIndexSearcher(reader, getAccessControlFactory(), readAuthorizations,
+        discoverAuthorizations, toSet(discoverableFields));
+    TopDocs topDocs;
+    Query query = new MatchAllDocsQuery();
+    {
+      topDocs = searcher.search(query, 10);
+      assertEquals(expected, topDocs.totalHits);
+    }
+    DocumentAuthorizations readDocumentAuthorizations = new DocumentAuthorizations(readAuthorizations);
+    DocumentAuthorizations discoverDocumentAuthorizations = new DocumentAuthorizations(discoverAuthorizations);
+    DocumentVisibilityEvaluator readVisibilityEvaluator = new DocumentVisibilityEvaluator(readDocumentAuthorizations);
+    DocumentVisibilityEvaluator discoverVisibilityEvaluator = new DocumentVisibilityEvaluator(
+        discoverDocumentAuthorizations);
+    for (int i = 0; i < topDocs.totalHits & i < topDocs.scoreDocs.length; i++) {
+      Document doc = searcher.doc(topDocs.scoreDocs[i].doc);
+      String read = doc.get("_read_");
+      String discover = doc.get("_discover_");
+      if (read != null && discover != null) {
+        DocumentVisibility readVisibility = new DocumentVisibility(read);
+        DocumentVisibility discoverVisibility = new DocumentVisibility(discover);
+        assertTrue(readVisibilityEvaluator.evaluate(readVisibility)
+            || discoverVisibilityEvaluator.evaluate(discoverVisibility));
+      } else if (read != null) {
+        DocumentVisibility readVisibility = new DocumentVisibility(read);
+        assertTrue(readVisibilityEvaluator.evaluate(readVisibility));
+      } else if (discover != null) {
+        DocumentVisibility discoverVisibility = new DocumentVisibility(discover);
+        assertTrue(discoverVisibilityEvaluator.evaluate(discoverVisibility));
+        // Since this document is only discoverable validate fields that are
+        // being returned.
+        validateDiscoverFields(doc, discoverableFields);
+      } else {
+        fail("Should not fetch empty document.");
+      }
+    }
+    searcher.search(query, new Collector() {
+
+      @Override
+      public void setScorer(Scorer scorer) throws IOException {
+      }
+
+      @Override
+      public void setNextReader(AtomicReaderContext context) throws IOException {
+        assertTrue(context.reader() instanceof SecureAtomicReader);
+      }
+
+      @Override
+      public void collect(int doc) throws IOException {
+
+      }
+
+      @Override
+      public boolean acceptsDocsOutOfOrder() {
+        return false;
+      }
+    });
+  }
+
+  private Iterable<? extends IndexableField> getEmpty() {
+    return new Document();
+  }
+
+  private void validateDiscoverFields(Document doc, Collection<String> discoverableFields) {
+    Set<String> fields = new HashSet<String>(discoverableFields);
+    for (IndexableField indexableField : doc.getFields()) {
+      assertTrue(fields.contains(indexableField.name()));
+    }
+  }
+
+  private Set<String> toSet(Collection<String> col) {
+    if (col == null) {
+      return null;
+    }
+    return new HashSet<String>(col);
+  }
+
+  private AccessControlFactory getAccessControlFactory() {
+    return _accessControlFactory;
+  }
+
+  private Iterable<? extends IndexableField> getDoc(int docId, String read, String discover, String field1,
+      String field2) {
+    Document doc = new Document();
+    doc.add(new StringField("id", Integer.toString(docId), Store.YES));
+    AccessControlWriter writer = _accessControlFactory.getWriter();
+    doc.add(new StringField("f1", field1, Store.YES));
+    doc.add(new StringField("f2", field2, Store.YES));
+    doc.add(new TextField("text", "constant text", Store.YES));
+    Iterable<IndexableField> fields = doc;
+    if (read != null) {
+      fields = writer.addReadVisiblity(read, doc);
+    }
+    if (discover != null) {
+      fields = writer.addDiscoverVisiblity(discover, fields);
+    }
+    return fields;
+  }
+
+  private List<String> list(String... strs) {
+    return Arrays.asList(strs);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/49390b52/blur-document-security/src/test/java/org/apache/blur/lucene/security/LoadTest.java
----------------------------------------------------------------------
diff --git a/blur-document-security/src/test/java/org/apache/blur/lucene/security/LoadTest.java b/blur-document-security/src/test/java/org/apache/blur/lucene/security/LoadTest.java
new file mode 100644
index 0000000..80d589a
--- /dev/null
+++ b/blur-document-security/src/test/java/org/apache/blur/lucene/security/LoadTest.java
@@ -0,0 +1,122 @@
+/**
+ * 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;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Random;
+
+import org.apache.blur.lucene.security.index.AccessControlFactory;
+import org.apache.blur.lucene.security.index.AccessControlWriter;
+import org.apache.blur.lucene.security.index.FilterAccessControlFactory;
+import org.apache.blur.lucene.security.search.SecureIndexSearcher;
+import org.apache.lucene.analysis.core.KeywordAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.util.Version;
+
+public class LoadTest {
+
+  private static final long MAX_DOCS = 100000000;
+
+  public static void main(String[] args) throws IOException {
+    AccessControlFactory accessControlFactory = new FilterAccessControlFactory();
+    // AccessControlFactory accessControlFactory = new
+    // DocValueAccessControlFactory();
+    runTest(accessControlFactory);
+
+  }
+
+  private static void runTest(AccessControlFactory accessControlFactory) throws IOException {
+    File file = new File("./src/test/resouces/loadtestindex-" + accessControlFactory.getClass().getName());
+    FSDirectory directory = FSDirectory.open(file);
+    if (!file.exists() || !DirectoryReader.indexExists(directory)) {
+      long s = System.nanoTime();
+      createIndex(directory, accessControlFactory);
+      long e = System.nanoTime();
+      System.out.println("Index Creation Time [" + (e - s) / 1000000.0 + "]");
+    }
+    DirectoryReader reader = DirectoryReader.open(directory);
+
+    IndexSearcher searcher = new IndexSearcher(reader);
+
+    SecureIndexSearcher secureIndexSearcher1 = new SecureIndexSearcher(reader, accessControlFactory,
+        Arrays.asList("nothing"), Arrays.asList("nothing"), new HashSet<String>());
+
+    SecureIndexSearcher secureIndexSearcher2 = new SecureIndexSearcher(reader, accessControlFactory,
+        Arrays.asList("r1"), Arrays.asList("nothing"), new HashSet<String>());
+
+    MatchAllDocsQuery query = new MatchAllDocsQuery();
+    for (int p = 0; p < 10; p++) {
+      hitEnterToContinue();
+      runSearch(searcher, query);
+      hitEnterToContinue();
+      runSearch(secureIndexSearcher1, query);
+      hitEnterToContinue();
+      runSearch(secureIndexSearcher2, query);
+    }
+  }
+
+  private static void hitEnterToContinue() throws IOException {
+    // System.out.println("Hit Enter.");
+    // BufferedReader reader = new BufferedReader(new
+    // InputStreamReader(System.in));
+    // reader.readLine();
+  }
+
+  private static void runSearch(IndexSearcher searcher, MatchAllDocsQuery query) throws IOException {
+    long t1 = System.nanoTime();
+    TopDocs topDocs = searcher.search(query, 10);
+    long t2 = System.nanoTime();
+    System.out.println(topDocs.totalHits + " " + (t2 - t1) / 1000000.0);
+  }
+
+  private static void createIndex(Directory directory, AccessControlFactory accessControlFactory) throws IOException {
+    IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_43, new KeywordAnalyzer());
+    IndexWriter writer = new IndexWriter(directory, conf);
+
+    AccessControlWriter accessControlWriter = accessControlFactory.getWriter();
+    Random random = new Random(1);
+    for (long i = 0; i < MAX_DOCS; i++) {
+      if (i % 1000000 == 0) {
+        System.out.println("Building " + i);
+      }
+      writer.addDocument(accessControlWriter.addDiscoverVisiblity("d1",
+          accessControlWriter.addReadVisiblity("r1", getDoc(i, random))));
+    }
+    writer.close();
+  }
+
+  private static Iterable<IndexableField> getDoc(long i, Random random) {
+    Document document = new Document();
+    document.add(new StringField("f1", Integer.toString(random.nextInt(1000000)), Store.YES));
+    return document;
+  }
+
+}


Mime
View raw message