Repository: incubator-sentry
Updated Branches:
  refs/heads/master 03b2f1882 -> 59bbfdc79


SENTRY-989: RealTimeGet with explicit ids can bypass document level 
authorization (Gregory Chanan, reviewed by Hao Hao)


Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/59bbfdc7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/59bbfdc7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/59bbfdc7

Branch: refs/heads/master
Commit: 59bbfdc7983c5aec465b643d2584ad888432e48b
Parents: 03b2f18
Author: Gregory Chanan <gcha...@cloudera.com>
Authored: Thu Feb 18 16:20:52 2016 -0800
Committer: Gregory Chanan <gcha...@cloudera.com>
Committed: Mon Feb 22 14:24:16 2016 -0800

----------------------------------------------------------------------
 .../solr/handler/SecureRealTimeGetHandler.java  |  36 ++
 .../QueryDocAuthorizationComponent.java         |  55 ++-
 .../component/SecureRealTimeGetComponent.java   | 356 ++++++++++++++
 .../e2e/solr/AbstractSolrSentryTestBase.java    |  76 ++-
 .../tests/e2e/solr/DocLevelGenerator.java       |  72 +++
 .../tests/e2e/solr/TestDocLevelOperations.java  | 237 ++++-----
 .../sentry/tests/e2e/solr/TestRealTimeGet.java  | 476 +++++++++++++++++++
 .../resources/solr/collection1/conf/schema.xml  |   1 +
 .../collection1/conf/solrconfig-doclevel.xml    |  19 +-
 .../solr/sentry/test-authz-provider.ini         |   2 +-
 10 files changed, 1197 insertions(+), 133 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/59bbfdc7/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRealTimeGetHandler.java
----------------------------------------------------------------------
diff --git 
a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRealTimeGetHandler.java
 
b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRealTimeGetHandler.java
new file mode 100644
index 0000000..db182ef
--- /dev/null
+++ 
b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRealTimeGetHandler.java
@@ -0,0 +1,36 @@
+/*
+ * 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.solr.handler;
+
+
+import org.apache.solr.handler.component.RealTimeGetComponent;
+import org.apache.solr.handler.component.SecureRealTimeGetComponent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SecureRealTimeGetHandler extends RealTimeGetHandler {
+  @Override
+  protected List<String> getDefaultComponents()
+  {
+    List<String> names = new ArrayList<>(1);
+    names.add(RealTimeGetComponent.COMPONENT_NAME);
+    names.add(SecureRealTimeGetComponent.COMPONENT_NAME);
+    return names;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/59bbfdc7/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/QueryDocAuthorizationComponent.java
----------------------------------------------------------------------
diff --git 
a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/QueryDocAuthorizationComponent.java
 
b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/QueryDocAuthorizationComponent.java
index 666c088..be46a85 100644
--- 
a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/QueryDocAuthorizationComponent.java
+++ 
b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/QueryDocAuthorizationComponent.java
@@ -17,6 +17,12 @@
 
 package org.apache.solr.handler.component;
 
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.solr.common.SolrException;
@@ -69,6 +75,40 @@ public class QueryDocAuthorizationComponent extends 
SearchComponent
       .append(value).append("}");
   }
 
+  public String getFilterQueryStr(Set<String> roles) {
+    if (roles != null && roles.size() > 0) {
+      StringBuilder builder = new StringBuilder();
+      for (String role : roles) {
+        addRawClause(builder, authField, role);
+      }
+      if (allRolesToken != null && !allRolesToken.isEmpty()) {
+        addRawClause(builder, authField, allRolesToken);
+      }
+      return builder.toString();
+    }
+    return null;
+  }
+
+  private BooleanClause getBooleanClause(String authField, String value) {
+    Term t = new Term(authField, value);
+    return new BooleanClause(new TermQuery(t), BooleanClause.Occur.SHOULD);
+  }
+
+  public Query getFilterQuery(Set<String> roles) {
+    if (roles != null && roles.size() > 0) {
+      BooleanQuery query = new BooleanQuery();
+      for (String role : roles) {
+        query.add(getBooleanClause(authField, role));
+      }
+      if (allRolesToken != null && !allRolesToken.isEmpty()) {
+        query.add(getBooleanClause(authField, allRolesToken));
+      }
+      return query;
+    }
+
+    return null;
+  }
+
   @Override
   public void prepare(ResponseBuilder rb) throws IOException {
     if (!enabled) {
@@ -82,16 +122,9 @@ public class QueryDocAuthorizationComponent extends 
SearchComponent
     }
     Set<String> roles = sentryInstance.getRoles(userName);
     if (roles != null && roles.size() > 0) {
-      StringBuilder builder = new StringBuilder();
-      for (String role : roles) {
-        addRawClause(builder, authField, role);
-      }
-      if (allRolesToken != null && !allRolesToken.isEmpty()) {
-        addRawClause(builder, authField, allRolesToken);
-      }
+      String filterQuery = getFilterQueryStr(roles);
       ModifiableSolrParams newParams = new 
ModifiableSolrParams(rb.req.getParams());
-      String result = builder.toString();
-      newParams.add("fq", result);
+      newParams.add("fq", filterQuery);
       rb.req.setParams(newParams);
     } else {
       throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED,
@@ -113,4 +146,8 @@ public class QueryDocAuthorizationComponent extends 
SearchComponent
   public String getSource() {
     return "$URL$";
   }
+
+  public boolean getEnabled() {
+    return enabled;
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/59bbfdc7/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/SecureRealTimeGetComponent.java
----------------------------------------------------------------------
diff --git 
a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/SecureRealTimeGetComponent.java
 
b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/SecureRealTimeGetComponent.java
new file mode 100644
index 0000000..e692f54
--- /dev/null
+++ 
b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/SecureRealTimeGetComponent.java
@@ -0,0 +1,356 @@
+/*
+ * 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.solr.handler.component;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.util.BytesRef;
+
+import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.response.transform.DocTransformer;
+import org.apache.solr.response.transform.DocTransformers;
+import org.apache.solr.response.transform.TransformContext;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.SolrReturnFields;
+import org.apache.solr.search.ReturnFields;
+import org.apache.solr.sentry.SentryIndexAuthorizationSingleton;
+import org.apache.solr.update.AddUpdateCommand;
+import org.apache.solr.update.UpdateCommand;
+import org.apache.solr.update.UpdateLog;
+import org.apache.solr.util.RefCounted;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+public class SecureRealTimeGetComponent extends SearchComponent
+{
+  private static Logger log =
+    LoggerFactory.getLogger(SecureRealTimeGetComponent.class);
+  public static String ID_FIELD_NAME = "_reserved_sentry_id";
+  public static final String COMPONENT_NAME = "secureGet";
+
+  private SentryIndexAuthorizationSingleton sentryInstance;
+
+  public SecureRealTimeGetComponent() {
+    this(SentryIndexAuthorizationSingleton.getInstance());
+  }
+
+  @VisibleForTesting
+  public SecureRealTimeGetComponent(SentryIndexAuthorizationSingleton 
sentryInstance) {
+    super();
+    this.sentryInstance = sentryInstance;
+  }
+
+  @Override
+  public void prepare(ResponseBuilder rb) throws IOException {
+    QueryDocAuthorizationComponent docComponent =
+        
(QueryDocAuthorizationComponent)rb.req.getCore().getSearchComponent("queryDocAuthorization");
+    if (docComponent != null) {
+      String userName = sentryInstance.getUserName(rb.req);
+      String superUser = (System.getProperty("solr.authorization.superuser", 
"solr"));
+      // security is never applied to the super user; for example, if solr 
internally is using
+      // real time get for replica synchronization, we need to return all the 
documents.
+      if (docComponent.getEnabled() && !superUser.equals(userName)) {
+        Set<String> roles = sentryInstance.getRoles(userName);
+        if (roles != null && roles.size() > 0) {
+          SolrReturnFields savedReturnFields = 
(SolrReturnFields)rb.rsp.getReturnFields();
+          if (savedReturnFields == null) {
+            throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+                "Not able to authorize request because ReturnFields is 
invalid: " + savedReturnFields);
+          }
+          DocTransformer savedTransformer = savedReturnFields.getTransformer();
+          Query filterQuery = docComponent.getFilterQuery(roles);
+          if (filterQuery != null) {
+            SolrReturnFields solrReturnFields = new 
AddDocIdReturnFields(rb.req, savedTransformer, filterQuery);
+            rb.rsp.setReturnFields(solrReturnFields);
+          } else {
+            throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED,
+              "Request from user: " + userName +
+              "rejected because filter query was unable to be generated");
+          }
+        } else {
+          throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED,
+            "Request from user: " + userName +
+            " rejected because user is not associated with any roles");
+        }
+      }
+    } else {
+      throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED,
+        "RealTimeGetRequest request " +
+        " rejected because \"queryDocAuthorization\" component not defined");
+    }
+  }
+
+  @Override
+  public void process(ResponseBuilder rb) throws IOException {
+    if (!(rb.rsp.getReturnFields() instanceof AddDocIdReturnFields)) {
+      log.info("Skipping application of SecureRealTimeGetComponent because "
+          + " return field wasn't applied in prepare phase");
+      return;
+    }
+
+    final SolrQueryResponse rsp = rb.rsp;
+    ResponseFormatDocs responseFormatDocs = getResponseFormatDocs(rsp);
+    if (responseFormatDocs == null) {
+      return; // no documents to check
+    }
+    final SolrDocumentList docList = responseFormatDocs.getDocList();
+    final AddDocIdReturnFields addDocIdRf = 
(AddDocIdReturnFields)rb.rsp.getReturnFields();
+    final Query filterQuery = addDocIdRf.getFilterQuery();
+    final DocTransformer transformer = addDocIdRf.getOriginalTransformer();
+
+    // we replaced the original transfer in order to add the document id, 
reapply it here
+    // so return documents in the correct format.
+    if (transformer != null) {
+      TransformContext context = new TransformContext();
+      context.req = rb.req;
+      transformer.setContext(context);
+    }
+
+    SolrCore core = rb.req.getCore();
+    UpdateLog ulog = core.getUpdateHandler().getUpdateLog();
+    SchemaField idField = core.getLatestSchema().getUniqueKeyField();
+    FieldType fieldType = idField.getType();
+    boolean openedRealTimeSearcher = false;
+    RefCounted<SolrIndexSearcher> searcherHolder = core.getRealtimeSearcher();
+
+    SolrDocumentList docListToReturn = new SolrDocumentList();
+    try {
+      SolrIndexSearcher searcher = searcherHolder.get();
+      for (SolrDocument doc : docList) {
+        // -1 doc id indicates this value was read from log; we need to open
+        // a new real time searcher to run the filter query against
+        if (doc.get(ID_FIELD_NAME) == -1 && !openedRealTimeSearcher) {
+          searcherHolder.decref();
+          // hack to clear ulog maps since we don't have
+          // openRealtimeSearcher API from SOLR-8436
+          AddUpdateCommand cmd = new AddUpdateCommand(rb.req);
+          cmd.setFlags(UpdateCommand.REPLAY);
+          ulog.add(cmd, true);
+
+          searcherHolder = core.getRealtimeSearcher();
+          searcher = searcherHolder.get();
+          openedRealTimeSearcher = true;
+        }
+
+        int docid = getFilteredInternalDocId(doc, idField, fieldType, 
filterQuery, searcher);
+        if (docid < 0) continue;
+        Document luceneDocument = searcher.doc(docid);
+        SolrDocument newDoc = toSolrDoc(luceneDocument,  
core.getLatestSchema());
+        if( transformer != null ) {
+          transformer.transform(newDoc, docid);
+        }
+        docListToReturn.add(newDoc);
+      }
+    } finally {
+      searcherHolder.decref();
+      searcherHolder = null;
+    }
+    if (responseFormatDocs.getUseResponseField()) {
+      rsp.getValues().remove("response");
+      docListToReturn.setNumFound(docListToReturn.size());
+      rsp.add("response", docListToReturn);
+    } else {
+      rsp.getValues().remove("doc");
+      rsp.add("doc", docListToReturn.size() > 0 ? docListToReturn.get(0) : 
null);
+    }
+  }
+
+  private static SolrDocument toSolrDoc(Document doc, IndexSchema schema) {
+    SolrDocument out = new SolrDocument();
+    for ( IndexableField f : doc.getFields() ) {
+      // Make sure multivalued fields are represented as lists
+      Object existing = out.get(f.name());
+      if (existing == null) {
+        SchemaField sf = schema.getFieldOrNull(f.name());
+
+        // don't return copyField targets
+        if (sf != null && schema.isCopyFieldTarget(sf)) continue;
+
+        if (sf != null && sf.multiValued()) {
+          List<Object> vals = new ArrayList<>();
+          vals.add( f );
+          out.setField( f.name(), vals );
+        }
+        else{
+          out.setField( f.name(), f );
+        }
+      }
+      else {
+        out.addField( f.name(), f );
+      }
+    }
+    return out;
+  }
+
+  // get the response format to use and the documents to check
+  private static ResponseFormatDocs getResponseFormatDocs(final 
SolrQueryResponse rsp) {
+    SolrDocumentList docList = 
(SolrDocumentList)rsp.getValues().get("response");
+    SolrDocument singleDoc = (SolrDocument)rsp.getValues().get("doc");
+    if (docList == null && singleDoc == null) {
+      return null; // no documents to filter
+    }
+    if (docList != null && singleDoc != null) {
+       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+          "Not able to filter secure reponse, RealTimeGet returned both a doc 
list and " +
+          "an individual document");
+    }
+    final boolean useResponseField = docList != null;
+    if (docList == null) {
+      docList = new SolrDocumentList();
+      docList.add(singleDoc);
+    }
+    return new ResponseFormatDocs(useResponseField, docList);
+  }
+
+  /**
+   * @param doc SolrDocument to check
+   * @param idField field where the id is stored
+   * @param fieldType type of id field
+   * @param filterQuery Query to filter by
+   * @param searcher SolrIndexSearcher on which to apply the filter query
+   * @returns the internal docid, or -1 if doc is not found or doesn't match 
filter
+   */
+  private static int getFilteredInternalDocId(SolrDocument doc, SchemaField 
idField, FieldType fieldType,
+        Query filterQuery, SolrIndexSearcher searcher) throws IOException {
+    int docid = -1;
+    Field f = (Field)doc.getFieldValue(idField.getName());
+    String idStr = f.stringValue();
+    BytesRef idBytes = new BytesRef();
+    fieldType.readableToIndexed(idStr, idBytes);
+    // get the internal document id
+    long segAndId = searcher.lookupId(idBytes);
+
+      // if docid is valid, run it through the filter
+    if (segAndId >= 0) {
+      int segid = (int) segAndId;
+      AtomicReaderContext ctx = 
searcher.getTopReaderContext().leaves().get((int) (segAndId >> 32));
+      docid = segid + ctx.docBase;
+      Weight weight = filterQuery.createWeight(searcher);
+      Scorer scorer = weight.scorer(ctx, null);
+      if (scorer == null || segid != scorer.advance(segid)) {
+        // filter doesn't match.
+        docid = -1;
+      }
+    }
+    return docid;
+  }
+
+  @Override
+  public String getDescription() {
+    return "Handle Query Document Authorization for RealTimeGet";
+  }
+
+  @Override
+  public String getSource() {
+    return "$URL$";
+  }
+
+  private static class ResponseFormatDocs {
+    private boolean useResponseField;
+    private SolrDocumentList docList;
+
+    public ResponseFormatDocs(boolean useResponseField, SolrDocumentList 
docList) {
+      this.useResponseField = useResponseField;
+      this.docList = docList;
+    }
+
+    public boolean getUseResponseField() { return useResponseField; }
+    public SolrDocumentList getDocList() { return docList; }
+  }
+
+  // ReturnField that adds a transformer to store the document id
+  private static class AddDocIdReturnFields extends SolrReturnFields {
+    private DocTransformer transformer;
+    private DocTransformer originalTransformer;
+    private Query filterQuery;
+
+    public AddDocIdReturnFields(SolrQueryRequest req, DocTransformer 
docTransformer,
+        Query filterQuery) {
+      super(req);
+      this.originalTransformer = docTransformer;
+      this.filterQuery = filterQuery;
+      final DocTransformers docTransformers = new DocTransformers();
+      if (originalTransformer != null) 
docTransformers.addTransformer(originalTransformer);
+      docTransformers.addTransformer(new DocIdAugmenter(ID_FIELD_NAME));
+      this.transformer = docTransformers;
+    }
+
+    @Override
+    public DocTransformer getTransformer() {
+      return transformer;
+    }
+
+    public DocTransformer getOriginalTransformer() {
+      return originalTransformer;
+    }
+
+    public Query getFilterQuery() {
+      return filterQuery;
+    }
+  }
+
+  // the Solr DocIdAugmenterFactory does not store negative doc ids;
+  // we do here.
+  private static class DocIdAugmenter extends DocTransformer
+  {
+    final String name;
+
+    public DocIdAugmenter( String display )
+    {
+      this.name = display;
+    }
+
+    @Override
+    public String getName()
+    {
+      return name;
+    }
+
+    @Override
+    public void transform(SolrDocument doc, int docid) {
+      doc.setField( name, docid );
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/59bbfdc7/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/AbstractSolrSentryTestBase.java
----------------------------------------------------------------------
diff --git 
a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/AbstractSolrSentryTestBase.java
 
b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/AbstractSolrSentryTestBase.java
index 2495a9e..3a2104a 100644
--- 
a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/AbstractSolrSentryTestBase.java
+++ 
b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/AbstractSolrSentryTestBase.java
@@ -28,6 +28,7 @@ import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.util.Comparator;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
@@ -61,6 +62,7 @@ import org.apache.solr.cloud.ZkController;
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrDocumentList;
 import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.Replica;
 import org.apache.solr.common.cloud.Slice;
@@ -90,6 +92,7 @@ public class AbstractSolrSentryTestBase {
   protected static final Random RANDOM = new Random();
   protected static final String RESOURCES_DIR = "target" + File.separator + 
"test-classes" + File.separator + "solr";
   protected static final String CONF_DIR_IN_ZK = "conf1";
+  protected static final String DEFAULT_COLLECTION = "collection1";
   protected static final int NUM_SERVERS = 4;
 
   private static void addPropertyToSentry(StringBuilder builder, String name, 
String value) {
@@ -413,17 +416,30 @@ public class AbstractSolrSentryTestBase {
    * @param solrUserName - User authenticated into Solr
    * @param adminOp - Admin operation to be performed
    * @param collectionName - Name of the collection to be queried
-   * @param ignoreError - boolean to specify whether to ignore the error if 
any occurred.
-   *                      (We may need this attribute for running DELETE 
command on a collection which doesn't exist)
    * @throws Exception
    */
   protected void verifyCollectionAdminOpPass(String solrUserName,
                                              CollectionAction adminOp,
                                              String collectionName) throws 
Exception {
+    verifyCollectionAdminOpPass(solrUserName, adminOp, collectionName, null);
+  }
+
+  /**
+   * Method to validate collection Admin operation pass
+   * @param solrUserName - User authenticated into Solr
+   * @param adminOp - Admin operation to be performed
+   * @param collectionName - Name of the collection to be queried
+   * @param params - SolrParams to use
+   * @throws Exception
+   */
+  protected void verifyCollectionAdminOpPass(String solrUserName,
+                                             CollectionAction adminOp,
+                                             String collectionName,
+                                             SolrParams params) throws 
Exception {
     String originalUser = getAuthenticatedUser();
     try {
       setAuthenticationUser(solrUserName);
-      QueryRequest request = populateCollectionAdminParams(adminOp, 
collectionName);
+      QueryRequest request = populateCollectionAdminParams(adminOp, 
collectionName, params);
       CloudSolrServer solrServer = createNewCloudSolrServer();
       try {
         NamedList<Object> result = solrServer.request(request);
@@ -449,12 +465,27 @@ public class AbstractSolrSentryTestBase {
   protected void verifyCollectionAdminOpFail(String solrUserName,
                                              CollectionAction adminOp,
                                              String collectionName) throws 
Exception {
+    verifyCollectionAdminOpFail(solrUserName, adminOp, collectionName, null);
+  }
+
+  /**
+   * Method to validate collection Admin operation fail
+   * @param solrUserName - User authenticated into Solr
+   * @param adminOp - Admin operation to be performed
+   * @param collectionName - Name of the collection to be queried
+   * @param params - SolrParams to use
+   * @throws Exception
+   */
+  protected void verifyCollectionAdminOpFail(String solrUserName,
+                                             CollectionAction adminOp,
+                                             String collectionName,
+                                             SolrParams params) throws 
Exception {
 
     String originalUser = getAuthenticatedUser();
     try {
       setAuthenticationUser(solrUserName);
       try {
-        QueryRequest request = populateCollectionAdminParams(adminOp, 
collectionName);
+        QueryRequest request = populateCollectionAdminParams(adminOp, 
collectionName, params);
         CloudSolrServer solrServer = createNewCloudSolrServer();
         try {
           NamedList<Object> result = solrServer.request(request);
@@ -483,7 +514,20 @@ public class AbstractSolrSentryTestBase {
    * @return - instance of QueryRequest.
    */
   public QueryRequest populateCollectionAdminParams(CollectionAction adminOp,
-                                                            String 
collectionName) {
+                                                    String collectionName) {
+    return populateCollectionAdminParams(adminOp, collectionName, null);
+  }
+
+  /**
+   * Method to populate the Solr params based on the collection admin being 
performed.
+   * @param adminOp - Collection admin operation
+   * @param collectionName - Name of the collection
+   * @param params - SolrParams to use
+   * @return - instance of QueryRequest.
+   */
+  public QueryRequest populateCollectionAdminParams(CollectionAction adminOp,
+                                                    String collectionName,
+                                                    SolrParams params) {
     ModifiableSolrParams modParams = new ModifiableSolrParams();
     modParams.set(CoreAdminParams.ACTION, adminOp.name());
     switch (adminOp) {
@@ -519,6 +563,14 @@ public class AbstractSolrSentryTestBase {
         throw new IllegalArgumentException("Admin operation: " + adminOp + " 
is not supported!");
     }
 
+    if (params != null) {
+      Iterator<String> it = params.getParameterNamesIterator();
+      while (it.hasNext()) {
+        String param = it.next();
+        String [] value = params.getParams(param);
+        modParams.set(param, value);
+      }
+    }
     QueryRequest request = new QueryRequest(modParams);
     request.setPath("/admin/collections");
     return request;
@@ -701,16 +753,22 @@ public class AbstractSolrSentryTestBase {
   }
 
   protected void uploadConfigDirToZk(String collectionConfigDir) throws 
Exception {
+    uploadConfigDirToZk(collectionConfigDir, CONF_DIR_IN_ZK);
+  }
+
+  protected void uploadConfigDirToZk(String collectionConfigDir, String 
confDirInZk) throws Exception {
     ZkController zkController = getZkController();
-    // conf1 is the config used by AbstractFullDistribZkTestBase
-    zkController.uploadConfigDir(new File(collectionConfigDir),
-      CONF_DIR_IN_ZK);
+    zkController.uploadConfigDir(new File(collectionConfigDir), confDirInZk);
   }
 
   protected void uploadConfigFileToZk(String file, String nameInZk) throws 
Exception {
+    uploadConfigFileToZk(file, nameInZk, CONF_DIR_IN_ZK);
+  }
+
+  protected void uploadConfigFileToZk(String file, String nameInZk, String 
confDirInZk) throws Exception {
     ZkController zkController = getZkController();
     zkController.getZkClient().makePath(ZkController.CONFIGS_ZKNODE + "/"
-      + CONF_DIR_IN_ZK + "/" + nameInZk, new File(file), false, true);
+      + confDirInZk + "/" + nameInZk, new File(file), false, true);
   }
 
   protected CloudSolrServer createNewCloudSolrServer() throws Exception {

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/59bbfdc7/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/DocLevelGenerator.java
----------------------------------------------------------------------
diff --git 
a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/DocLevelGenerator.java
 
b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/DocLevelGenerator.java
new file mode 100644
index 0000000..30afd4c
--- /dev/null
+++ 
b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/DocLevelGenerator.java
@@ -0,0 +1,72 @@
+/*
+ * 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.sentry.tests.e2e.solr;
+
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.client.solrj.impl.CloudSolrServer;
+
+import java.util.ArrayList;
+
+public class DocLevelGenerator {
+  private String collection;
+  private String authField;
+
+  public DocLevelGenerator(String collection, String authField) {
+    this.collection = collection;
+    this.authField = authField;
+  }
+
+  /**
+   * Generates docs according to the following parameters:
+   *
+   * @param server SolrServer to use
+   * @param numDocs number of documents to generate
+   * @param evenDocsToken every even number doc gets this token added to the 
authField
+   * @param oddDocsToken every odd number doc gets this token added to the 
authField
+   * @param extraAuthFieldsCount generates this number of bogus entries in the 
authField
+   */
+  public void generateDocs(CloudSolrServer server, int numDocs, String 
evenDocsToken, String oddDocsToken, int extraAuthFieldsCount) throws Exception {
+
+    // create documents
+    ArrayList<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
+    for (int i = 0; i < numDocs; ++i) {
+      SolrInputDocument doc = new SolrInputDocument();
+      String iStr = Long.toString(i);
+      doc.addField("id", iStr);
+      doc.addField("description", "description" + iStr);
+
+      // put some bogus tokens in
+      for (int k = 0; k < extraAuthFieldsCount; ++k) {
+        doc.addField(authField, authField + Long.toString(k));
+      }
+      // even docs get evenDocsToken, odd docs get oddDocsToken
+      if (i % 2 == 0) {
+        doc.addField(authField, evenDocsToken);
+      } else {
+        doc.addField(authField, oddDocsToken);
+      }
+      // add a token to all docs so we can check that we can get all
+      // documents returned
+      doc.addField(authField, "docLevel_role");
+
+      docs.add(doc);
+    }
+
+    server.add(docs);
+    server.commit(true, true);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/59bbfdc7/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestDocLevelOperations.java
----------------------------------------------------------------------
diff --git 
a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestDocLevelOperations.java
 
b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestDocLevelOperations.java
index ff508e1..46399df 100644
--- 
a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestDocLevelOperations.java
+++ 
b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestDocLevelOperations.java
@@ -25,11 +25,15 @@ import static org.junit.Assert.assertTrue;
 
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.impl.CloudSolrServer;
+import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
 
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrDocumentList;
 import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
 
 import java.io.File;
 import java.net.URLEncoder;
@@ -44,7 +48,6 @@ import org.junit.Test;
 public class TestDocLevelOperations extends AbstractSolrSentryTestBase {
   private static final Logger LOG = LoggerFactory
     .getLogger(TestDocLevelOperations.class);
-  private static final String DEFAULT_COLLECTION = "collection1";
   private static final String AUTH_FIELD = "sentry_auth";
   private static final int NUM_DOCS = 100;
   private static final int EXTRA_AUTH_FIELDS = 2;
@@ -70,6 +73,31 @@ public class TestDocLevelOperations extends 
AbstractSolrSentryTestBase {
     setupCollection(name);
   }
 
+  private QueryRequest getRealTimeGetRequest() {
+    // real time get request
+    StringBuilder idsBuilder = new StringBuilder("0");
+    for (int i = 1; i < NUM_DOCS; ++i) {
+      idsBuilder.append("," + i);
+    }
+    return getRealTimeGetRequest(idsBuilder.toString());
+  }
+
+  private QueryRequest getRealTimeGetRequest(String ids) {
+    final ModifiableSolrParams idsParams = new ModifiableSolrParams();
+    idsParams.add("ids", ids);
+    return new QueryRequest() {
+      @Override
+      public String getPath() {
+        return "/get";
+      }
+
+      @Override
+      public SolrParams getParams() {
+        return idsParams;
+      }
+    };
+  }
+
   /**
    * Creates docs as follows and verifies queries work as expected:
    * - creates NUM_DOCS documents, where the document id equals the order
@@ -84,67 +112,45 @@ public class TestDocLevelOperations extends 
AbstractSolrSentryTestBase {
     // ensure no current documents
     verifyDeletedocsPass(ADMIN_USER, collectionName, true);
 
-    // create documents
-    ArrayList<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
-    for (int i = 0; i < NUM_DOCS; ++i) {
-      SolrInputDocument doc = new SolrInputDocument();
-      String iStr = Long.toString(i);
-      doc.addField("id", iStr);
-      doc.addField("description", "description" + iStr);
-
-      // put some bogus tokens in
-      for (int k = 0; k < EXTRA_AUTH_FIELDS; ++k) {
-        doc.addField(AUTH_FIELD, AUTH_FIELD + Long.toString(k));
-      }
-      // 50% of docs get "junit", 50% get "admin" as token
-      if (i % 2 == 0) {
-        doc.addField(AUTH_FIELD, "junit_role");
-      } else {
-        doc.addField(AUTH_FIELD, "admin_role");
-      }
-      // add a token to all docs so we can check that we can get all
-      // documents returned
-      doc.addField(AUTH_FIELD, "docLevel_role");
-
-      docs.add(doc);
-    }
     CloudSolrServer server = getCloudSolrServer(collectionName);
     try {
-      server.add(docs);
-      server.commit(true, true);
+      DocLevelGenerator generator = new DocLevelGenerator(collectionName, 
AUTH_FIELD);
+      generator.generateDocs(server, NUM_DOCS, "junit_role", "admin_role", 
EXTRA_AUTH_FIELDS);
 
-      // queries
-      SolrQuery query = new SolrQuery();
-      query.setQuery("*:*");
+      querySimple(new QueryRequest(new SolrQuery("*:*")), server, 
checkNonAdminUsers);
+      querySimple(getRealTimeGetRequest(), server, checkNonAdminUsers);
+    } finally {
+      server.shutdown();
+    }
+  }
 
-      // as admin  -- should get the other half
-      setAuthenticationUser("admin");
-      QueryResponse  rsp = server.query(query);
-      SolrDocumentList docList = rsp.getResults();
+  private void querySimple(QueryRequest request, CloudSolrServer server,
+      boolean checkNonAdminUsers) throws Exception {
+    // as admin  -- should get the other half
+    setAuthenticationUser("admin");
+    QueryResponse  rsp = request.process(server);
+    SolrDocumentList docList = rsp.getResults();
+    assertEquals(NUM_DOCS / 2, docList.getNumFound());
+    for (SolrDocument doc : docList) {
+      String id = doc.getFieldValue("id").toString();
+      assertEquals(1, Long.valueOf(id) % 2);
+    }
+ 
+    if (checkNonAdminUsers) {
+      // as junit -- should get half the documents
+      setAuthenticationUser("junit");
+      rsp = request.process(server);
+      docList = rsp.getResults();
       assertEquals(NUM_DOCS / 2, docList.getNumFound());
       for (SolrDocument doc : docList) {
         String id = doc.getFieldValue("id").toString();
-        assertEquals(1, Long.valueOf(id) % 2);
+        assertEquals(0, Long.valueOf(id) % 2);
       }
 
-      if (checkNonAdminUsers) {
-        // as junit -- should get half the documents
-        setAuthenticationUser("junit");
-        rsp = server.query(query);
-        docList = rsp.getResults();
-        assertEquals(NUM_DOCS / 2, docList.getNumFound());
-        for (SolrDocument doc : docList) {
-          String id = doc.getFieldValue("id").toString();
-          assertEquals(0, Long.valueOf(id) % 2);
-        }
-
-        // as docLevel -- should get all
-        setAuthenticationUser("docLevel");
-        rsp = server.query(query);
-        assertEquals(NUM_DOCS, rsp.getResults().getNumFound());
-      }
-    } finally {
-      server.shutdown();
+      // as docLevel -- should get all
+      setAuthenticationUser("docLevel");
+      rsp = request.process(server);
+      assertEquals(NUM_DOCS, rsp.getResults().getNumFound());
     }
   }
 
@@ -237,31 +243,10 @@ public class TestDocLevelOperations extends 
AbstractSolrSentryTestBase {
         server.add(docs);
         server.commit(true, true);
 
-        // queries
-        SolrQuery query = new SolrQuery();
-        query.setQuery("*:*");
-
-        // as admin  -- should only get all roles token documents
-        setAuthenticationUser("admin");
-        QueryResponse rsp = server.query(query);
-        SolrDocumentList docList = rsp.getResults();
-        assertEquals(totalAllRolesAdded, docList.getNumFound());
-        for (SolrDocument doc : docList) {
-          String id = doc.getFieldValue("id").toString();
-          assertEquals(0, Long.valueOf(id) % allRolesFactor);
-        }
-
-        // as junit -- should get junit added + onlyAllRolesAdded
-        setAuthenticationUser("junit");
-        rsp = server.query(query);
-        docList = rsp.getResults();
-        assertEquals(totalJunitAdded + totalOnlyAllRolesAdded, 
docList.getNumFound());
-        for (SolrDocument doc : docList) {
-          String id = doc.getFieldValue("id").toString();
-          boolean addedJunit = (Long.valueOf(id) % junitFactor) == 0;
-          boolean onlyAllRoles = !addedJunit && (Long.valueOf(id) % 
allRolesFactor) == 0;
-          assertEquals(true, addedJunit || onlyAllRoles);
-        }
+        checkAllRolesToken(new QueryRequest(new SolrQuery("*:*")), server,
+            totalAllRolesAdded, totalOnlyAllRolesAdded, allRolesFactor, 
totalJunitAdded, junitFactor);
+        checkAllRolesToken(getRealTimeGetRequest(), server,
+             totalAllRolesAdded, totalOnlyAllRolesAdded, allRolesFactor, 
totalJunitAdded, junitFactor);
       } finally {
         server.shutdown();
       }
@@ -270,6 +255,31 @@ public class TestDocLevelOperations extends 
AbstractSolrSentryTestBase {
     }
   }
 
+  private void checkAllRolesToken(QueryRequest request, CloudSolrServer server,
+      int totalAllRolesAdded, int totalOnlyAllRolesAdded, int allRolesFactor, 
int totalJunitAdded, int junitFactor) throws Exception {
+    // as admin  -- should only get all roles token documents
+    setAuthenticationUser("admin");
+    QueryResponse rsp = request.process(server);
+    SolrDocumentList docList = rsp.getResults();
+    assertEquals(totalAllRolesAdded, docList.getNumFound());
+    for (SolrDocument doc : docList) {
+      String id = doc.getFieldValue("id").toString();
+      assertEquals(0, Long.valueOf(id) % allRolesFactor);
+    }
+
+    // as junit -- should get junit added + onlyAllRolesAdded
+    setAuthenticationUser("junit");
+    rsp = request.process(server);
+    docList = rsp.getResults();
+    assertEquals(totalJunitAdded + totalOnlyAllRolesAdded, 
docList.getNumFound());
+    for (SolrDocument doc : docList) {
+      String id = doc.getFieldValue("id").toString();
+      boolean addedJunit = (Long.valueOf(id) % junitFactor) == 0;
+      boolean onlyAllRoles = !addedJunit && (Long.valueOf(id) % 
allRolesFactor) == 0;
+      assertEquals(true, addedJunit || onlyAllRoles);
+    }
+  }
+
   /**
    * delete the docs as "deleteUser" using deleteByQuery "deleteQueryStr".
    * Verify that number of docs returned for "queryUser" equals
@@ -280,32 +290,35 @@ public class TestDocLevelOperations extends 
AbstractSolrSentryTestBase {
     createDocsAndQuerySimple(collectionName, true);
     CloudSolrServer server = getCloudSolrServer(collectionName);
     try {
-      SolrQuery query = new SolrQuery();
-      query.setQuery("*:*");
-
       setAuthenticationUser(deleteUser);
       server.deleteByQuery(deleteByQueryStr);
       server.commit();
-      QueryResponse rsp =  server.query(query);
-      long junitResults = rsp.getResults().getNumFound();
-      assertEquals(0, junitResults);
-
-      setAuthenticationUser(queryUser);
-      rsp =  server.query(query);
-      long docLevelResults = rsp.getResults().getNumFound();
-      assertEquals(expectedQueryDocs, docLevelResults);
+
+      checkDeleteByQuery(new QueryRequest(new SolrQuery("*:*")), server,
+          queryUser, expectedQueryDocs);
+      checkDeleteByQuery(getRealTimeGetRequest(), server,
+          queryUser, expectedQueryDocs);
     } finally {
       server.shutdown();
     }
   }
 
+  private void checkDeleteByQuery(QueryRequest query, CloudSolrServer server,
+      String queryUser, int expectedQueryDocs) throws Exception {
+    QueryResponse rsp =  query.process(server);
+    long junitResults = rsp.getResults().getNumFound();
+    assertEquals(0, junitResults);
+
+    setAuthenticationUser(queryUser);
+    rsp =  query.process(server);
+    long docLevelResults = rsp.getResults().getNumFound();
+    assertEquals(expectedQueryDocs, docLevelResults);
+  }
+
   private void deleteByIdTest(String collectionName) throws Exception {
     createDocsAndQuerySimple(collectionName, true);
     CloudSolrServer server = getCloudSolrServer(collectionName);
     try {
-      SolrQuery query = new SolrQuery();
-      query.setQuery("*:*");
-
       setAuthenticationUser("junit");
       List<String> allIds = new ArrayList<String>(NUM_DOCS);
       for (int i = 0; i < NUM_DOCS; ++i) {
@@ -314,19 +327,25 @@ public class TestDocLevelOperations extends 
AbstractSolrSentryTestBase {
       server.deleteById(allIds);
       server.commit();
 
-      QueryResponse rsp =  server.query(query);
-      long junitResults = rsp.getResults().getNumFound();
-      assertEquals(0, junitResults);
-
-      setAuthenticationUser("docLevel");
-      rsp =  server.query(query);
-      long docLevelResults = rsp.getResults().getNumFound();
-      assertEquals(0, docLevelResults);
+      checkDeleteById(new QueryRequest(new SolrQuery("*:*")), server);
+      checkDeleteById(getRealTimeGetRequest(), server);
     } finally {
       server.shutdown();
     }
   }
 
+  private void checkDeleteById(QueryRequest request, CloudSolrServer server)
+      throws Exception {
+    QueryResponse rsp = request.process(server);
+    long junitResults = rsp.getResults().getNumFound();
+    assertEquals(0, junitResults);
+
+    setAuthenticationUser("docLevel");
+    rsp =  request.process(server);
+    long docLevelResults = rsp.getResults().getNumFound();
+    assertEquals(0, docLevelResults);
+  }
+
   private void updateDocsTest(String collectionName) throws Exception {
     createDocsAndQuerySimple(collectionName, true);
     CloudSolrServer server = getCloudSolrServer(collectionName);
@@ -335,10 +354,10 @@ public class TestDocLevelOperations extends 
AbstractSolrSentryTestBase {
       String docIdStr = Long.toString(1);
 
       // verify we can't view one of the odd documents
-      SolrQuery query = new SolrQuery();
-      query.setQuery("id:"+docIdStr);
-      QueryResponse rsp = server.query(query);
-      assertEquals(0, rsp.getResults().getNumFound());
+      QueryRequest query = new QueryRequest(new SolrQuery("id:"+docIdStr));
+      QueryRequest rtgQuery = getRealTimeGetRequest(docIdStr);
+      checkUpdateDocsQuery(query, server, 0);
+      checkUpdateDocsQuery(rtgQuery, server, 0);
 
       // overwrite the document that we can't see
       ArrayList<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
@@ -351,13 +370,19 @@ public class TestDocLevelOperations extends 
AbstractSolrSentryTestBase {
       server.commit();
 
       // verify we can now view the document
-      rsp = server.query(query);
-      assertEquals(1, rsp.getResults().getNumFound());
+      checkUpdateDocsQuery(query, server, 1);
+      checkUpdateDocsQuery(rtgQuery, server, 1);
     } finally {
       server.shutdown();
     }
   }
 
+  private void checkUpdateDocsQuery(QueryRequest request, CloudSolrServer 
server, int expectedDocs)
+      throws Exception {
+    QueryResponse rsp = request.process(server);
+    assertEquals(expectedDocs, rsp.getResults().getNumFound());
+  }
+
   @Test
   public void testUpdateDeleteOperations() throws Exception {
     String collectionName = "testUpdateDeleteOperations";

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/59bbfdc7/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestRealTimeGet.java
----------------------------------------------------------------------
diff --git 
a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestRealTimeGet.java
 
b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestRealTimeGet.java
new file mode 100644
index 0000000..0d25562
--- /dev/null
+++ 
b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestRealTimeGet.java
@@ -0,0 +1,476 @@
+/*
+ * 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.sentry.tests.e2e.solr;
+
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.CloudSolrServer;
+import org.apache.solr.client.solrj.request.QueryRequest;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.params.CollectionParams.CollectionAction;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.SolrParams;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+public class TestRealTimeGet extends AbstractSolrSentryTestBase {
+  private static final Logger LOG = LoggerFactory
+    .getLogger(TestRealTimeGet.class);
+  private static final String AUTH_FIELD = "sentry_auth";
+  private static final Random rand = new Random();
+  private String userName = null;
+
+  @Before
+  public void beforeTest() throws Exception {
+    userName = getAuthenticatedUser();
+  }
+
+  @After
+  public void afterTest() throws Exception {
+    setAuthenticationUser(userName);
+  }
+
+  private void setupCollectionWithDocSecurity(String name) throws Exception {
+    setupCollectionWithDocSecurity(name, 2);
+  }
+
+  private void setupCollectionWithDocSecurity(String name, int shards) throws 
Exception {
+    String configDir = RESOURCES_DIR + File.separator + DEFAULT_COLLECTION
+      + File.separator + "conf";
+    uploadConfigDirToZk(configDir, name);
+    // replace solrconfig.xml with solrconfig-doc-level.xml
+    uploadConfigFileToZk(configDir + File.separator + 
"solrconfig-doclevel.xml",
+        "solrconfig.xml", name);
+    ModifiableSolrParams modParams = new ModifiableSolrParams();
+    modParams.set("numShards", shards);
+    StringBuilder builder = new StringBuilder();
+    for (int i = 0; i < shards; ++i) {
+      if (i != 0) builder.append(",");
+      builder.append("shard").append(i+1);
+    }
+    modParams.set("shards", builder.toString());
+    verifyCollectionAdminOpPass(ADMIN_USER, CollectionAction.CREATE, name, 
modParams);
+  }
+
+  private void setupCollectionWithoutDocSecurity(String name) throws Exception 
{
+    String configDir = RESOURCES_DIR + File.separator + DEFAULT_COLLECTION
+      + File.separator + "conf";
+    uploadConfigDirToZk(configDir, name);
+    setupCollection(name);
+  }
+
+  private QueryRequest getRealTimeGetRequest(final SolrParams params) {
+    return new QueryRequest() {
+      @Override
+      public String getPath() {
+        return "/get";
+      }
+
+      @Override
+      public SolrParams getParams() {
+        return params;
+      }
+    };
+  }
+
+  private void assertExpected(ExpectedResult expectedResult, QueryResponse rsp,
+        ExpectedResult controlExpectedResult, QueryResponse controlRsp) throws 
Exception {
+    SolrDocumentList docList = rsp.getResults();
+    SolrDocumentList controlDocList = controlRsp.getResults();
+    SolrDocument doc = (SolrDocument)rsp.getResponse().get("doc");
+    SolrDocument controlDoc = 
(SolrDocument)controlRsp.getResponse().get("doc");
+
+    if (expectedResult.expectedDocs == 0) {
+      // could be null rather than 0 size, check against control that format 
is identical
+      assertNull("Should be no doc present: " + doc, doc);
+      assertNull("Should be no doc present: " + controlDoc, controlDoc);
+      assertTrue((docList == null && controlDocList == null) ||
+          (controlDocList.getNumFound() == 0 && controlDocList.getNumFound() 
== 0));
+    } else {
+      if (docList == null) {
+        assertNull(controlDocList);
+        assertNotNull(doc);
+        assertNotNull(controlDoc);
+      } else {
+        assertNotNull(controlDocList);
+        assertNull(doc);
+        assertNull(controlDoc);
+        assertEquals(expectedResult.expectedDocs, docList.getNumFound());
+        assertEquals(docList.getNumFound(), controlDocList.getNumFound());
+      }
+    }
+  }
+
+  private QueryResponse getIdResponse(ExpectedResult expectedResult) throws 
Exception {
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    for (int i = 0; i < expectedResult.ids.length; ++i) {
+      params.add("id", expectedResult.ids[ i ]);
+    }
+    if (expectedResult.fl != null) {
+      params.add("fl", expectedResult.fl);
+    }
+    QueryRequest request = getRealTimeGetRequest(params);
+    return request.process(expectedResult.server);
+  }
+
+  private QueryResponse getIdsResponse(ExpectedResult expectedResult) throws 
Exception {
+    StringBuilder builder = new StringBuilder();
+    for (int i = 0; i < expectedResult.ids.length; ++i) {
+      if (i != 0) builder.append(",");
+      builder.append(expectedResult.ids[ i ]);
+    }
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    params.add("ids", builder.toString());
+    if (expectedResult.fl != null) {
+      params.add("fl", expectedResult.fl);
+    }
+    QueryRequest request = getRealTimeGetRequest(params);
+    return request.process(expectedResult.server);
+  }
+
+  private void assertIdVsIds(ExpectedResult expectedResult, ExpectedResult 
controlExpectedResult)
+      throws Exception {
+    // test specifying with "id"
+    QueryResponse idRsp = getIdResponse(expectedResult);
+    QueryResponse idControlRsp = getIdResponse(controlExpectedResult);
+    assertExpected(expectedResult, idRsp, controlExpectedResult, idControlRsp);
+
+    // test specifying with "ids"
+    QueryResponse idsRsp = getIdsResponse(expectedResult);
+    QueryResponse idsControlRsp = getIdsResponse(controlExpectedResult);
+    assertExpected(expectedResult, idsRsp, controlExpectedResult, 
idsControlRsp);
+  }
+
+  @Test
+  public void testIdvsIds() throws Exception {
+    final String collection = "testIdvsIds";
+    final String collectionControl = collection + "Control";
+    setupCollectionWithDocSecurity(collection);
+    setupCollectionWithoutDocSecurity(collectionControl);
+    CloudSolrServer server = getCloudSolrServer(collection);
+    CloudSolrServer serverControl = getCloudSolrServer(collectionControl);
+
+    try {
+      for (CloudSolrServer s : new CloudSolrServer [] {server, serverControl}) 
{
+        DocLevelGenerator generator = new 
DocLevelGenerator(s.getDefaultCollection(), AUTH_FIELD);
+        generator.generateDocs(s, 100, "junit_role", "admin_role", 2);
+      }
+
+      // check that control collection does not filter
+      assertIdVsIds(new ExpectedResult(serverControl, new String[] {"2"}, 1),
+          new ExpectedResult(serverControl, new String[] {"2"}, 1));
+
+      // single id
+      assertIdVsIds(new ExpectedResult(server, new String[] {"1"}, 1),
+          new ExpectedResult(serverControl, new String[] {"1"}, 1));
+
+      // single id (invalid)
+      assertIdVsIds(new ExpectedResult(server, new String[] {"bogusId"}, 0),
+          new ExpectedResult(serverControl, new String[] {"bogusId"}, 0));
+
+      // single id (no permission)
+      assertIdVsIds(new ExpectedResult(server, new String[] {"2"}, 0),
+          new ExpectedResult(serverControl, new String[] {"2fake"}, 0));
+
+      // multiple ids (some invalid, some valid, some no permission)
+      assertIdVsIds(new ExpectedResult(server, new String[] {"bogus1", "1", 
"2"}, 1),
+          new ExpectedResult(serverControl, new String[] {"bogus1", "1", 
"bogus2"}, 1));
+      assertIdVsIds(new ExpectedResult(server, new String[] {"bogus1", "1", 
"2", "3"}, 2),
+          new ExpectedResult(serverControl, new String[] {"bogus1", "1", 
"bogus2", "3"}, 2));
+
+      // multiple ids (all invalid)
+      assertIdVsIds(new ExpectedResult(server, new String[] {"bogus1", 
"bogus2", "bogus3"}, 0),
+          new ExpectedResult(serverControl, new String[] {"bogus1", "bogus2", 
"bogus3"}, 0));
+
+      // multiple ids (all no permission)
+      assertIdVsIds(new ExpectedResult(server, new String[] {"2", "4", "6"}, 
0),
+          new ExpectedResult(serverControl, new String[] {"bogus2", "bogus4", 
"bogus6"}, 0));
+
+    } finally {
+      server.shutdown();
+      serverControl.shutdown();
+    }
+  }
+
+  private void assertFlOnDocList(SolrDocumentList list, Set<String> 
expectedIds,
+      List<String> expectedFields) {
+    assertEquals("Doc list size should be: " + expectedIds.size(), 
expectedIds.size(), list.getNumFound());
+    for (SolrDocument doc : list) {
+      expectedIds.contains(doc.get("id"));
+      for (String field : expectedFields) {
+        assertNotNull("Field: " + field + " should not be null in doc: " + 
doc, doc.get(field));
+      }
+      assertEquals("doc should have: " + expectedFields.size() + " fields.  
Doc: " + doc,
+          expectedFields.size(), doc.getFieldNames().size());
+    }
+  }
+
+  private void assertFl(CloudSolrServer server, String [] ids, Set<String> 
expectedIds,
+        String fl, List<String> expectedFields) throws Exception {
+    {
+      QueryResponse idRsp = getIdResponse(new ExpectedResult(server, ids, 
expectedIds.size(), fl));
+      SolrDocumentList idList = idRsp.getResults();
+      assertFlOnDocList(idList, expectedIds, expectedFields);
+    }
+    {
+      QueryResponse idsRsp = getIdsResponse(new ExpectedResult(server, ids, 
expectedIds.size(), fl));
+      SolrDocumentList idsList = idsRsp.getResults();
+      assertFlOnDocList(idsList, expectedIds, expectedFields);
+    }
+  }
+
+  @Test
+  public void testFl() throws Exception {
+    final String collection = "testFl";
+    // FixMe: have to use one shard, because of a Solr bug where "fl" is not 
applied to
+    // multi-shard get requests
+    setupCollectionWithDocSecurity(collection, 1);
+    CloudSolrServer server = getCloudSolrServer(collection);
+
+    try {
+      DocLevelGenerator generator = new DocLevelGenerator(collection, 
AUTH_FIELD);
+      generator.generateDocs(server, 100, "junit_role", "admin_role", 2);
+      String [] ids = new String[] {"1", "3", "5"};
+
+      assertFl(server, ids, new HashSet<String>(Arrays.asList(ids)), "id", 
Arrays.asList("id"));
+      assertFl(server, ids, new HashSet<String>(Arrays.asList(ids)), null, 
Arrays.asList("id", "description", "_version_"));
+      // test transformer
+      assertFl(server, ids, new HashSet<String>(Arrays.asList(ids)), 
"id,mydescription:description", Arrays.asList("id", "mydescription"));
+    } finally {
+      server.shutdown();
+    }
+  }
+
+  @Test
+  public void testNonCommitted() throws Exception {
+    final String collection = "testNonCommitted";
+    setupCollectionWithDocSecurity(collection, 1);
+    CloudSolrServer server = getCloudSolrServer(collection);
+
+    try {
+      DocLevelGenerator generator = new DocLevelGenerator(collection, 
AUTH_FIELD);
+      generator.generateDocs(server, 100, "junit_role", "admin_role", 2);
+
+      // make some uncommitted modifications and ensure they are reflected
+      server.deleteById("1");
+
+      SolrInputDocument doc2 = new SolrInputDocument();
+      doc2.addField("id", "2");
+      doc2.addField("description", "description2");
+      doc2.addField(AUTH_FIELD, "admin_role");
+ 
+      SolrInputDocument doc3 = new SolrInputDocument();
+      doc3.addField("id", "3");
+      doc3.addField("description", "description3");
+      doc3.addField(AUTH_FIELD, "junit_role");
+
+      SolrInputDocument doc200 = new SolrInputDocument();
+      doc200.addField("id", "200");
+      doc200.addField("description", "description200");
+      doc200.addField(AUTH_FIELD, "admin_role");
+      server.add(Arrays.asList(new SolrInputDocument [] {doc2, doc3, doc200}));
+
+      assertFl(server, new String[] {"1", "2", "3", "4", "5", "200"},
+          new HashSet<String>(Arrays.asList("2", "5", "200")), "id", 
Arrays.asList("id"));
+    } finally {
+      server.shutdown();
+    }
+  }
+
+  private void assertConcurrentOnDocList(SolrDocumentList list, String 
authField, String expectedAuthFieldValue) {
+    for (SolrDocument doc : list) {
+      Collection<Object> authFieldValues = doc.getFieldValues(authField);
+      assertNotNull(authField + " should not be null.  Doc: " + doc, 
authFieldValues);
+
+      boolean foundAuthFieldValue = false;
+      for (Object obj : authFieldValues) {
+        if (obj.toString().equals(expectedAuthFieldValue)) {
+          foundAuthFieldValue = true;
+          break;
+        }
+      }
+      assertTrue("Did not find: " + expectedAuthFieldValue + " in doc: " + 
doc, foundAuthFieldValue);
+    }
+  }
+
+  private void assertConcurrent(CloudSolrServer server, String [] ids, String 
authField, String expectedAuthFieldValue)
+      throws Exception {
+    {
+      QueryResponse idRsp = getIdResponse(new ExpectedResult(server, ids, -1, 
null));
+      SolrDocumentList idList = idRsp.getResults();
+      assertConcurrentOnDocList(idList, authField, expectedAuthFieldValue);
+    }
+    {
+      QueryResponse idsRsp = getIdsResponse(new ExpectedResult(server, ids, 
-1, null));
+      SolrDocumentList idsList = idsRsp.getResults();
+      assertConcurrentOnDocList(idsList, authField, expectedAuthFieldValue);
+    }
+  }
+
+  @Test
+  public void testConcurrentChanges() throws Exception {
+    final String collection = "testConcurrentChanges";
+    // Ensure the auth field is stored so we can check a consistent doc is 
returned
+    final String authField = "sentry_auth_stored";
+    System.setProperty("sentry.auth.field", authField);
+    setupCollectionWithDocSecurity(collection, 1);
+    CloudSolrServer server = getCloudSolrServer(collection);
+    int numQueries = 5;
+
+    try {
+      DocLevelGenerator generator = new DocLevelGenerator(collection, 
authField);
+      generator.generateDocs(server, 100, "junit_role", "admin_role", 2);
+
+      List<AuthFieldModifyThread> threads = new 
LinkedList<AuthFieldModifyThread>();
+      int docsToModify = 10;
+      for (int i = 0; i < docsToModify; ++i) {
+        SolrInputDocument doc = new SolrInputDocument();
+        doc.addField("id", Integer.toString(i));
+        doc.addField("description", "description" + Integer.toString(i));
+        doc.addField(authField, "junit_role");
+        server.add(doc);
+
+        threads.add(new AuthFieldModifyThread(server, doc,
+            authField, "junit_role", "admin_role"));
+      }
+      server.commit();
+
+      for (AuthFieldModifyThread thread : threads) {
+        thread.start();
+      }
+
+      // query
+      String [] ids = new String[docsToModify];
+      for (int j = 0; j < ids.length; ++j) {
+        ids[ j ] = Integer.toString(j);
+      }
+      for (int k = 0; k < numQueries; ++k) {
+        assertConcurrent(server, ids, authField, "admin_role");
+      }
+
+      for (AuthFieldModifyThread thread : threads) {
+        thread.setFinished();
+        thread.join();
+      }
+    } finally {
+      System.clearProperty("sentry.auth.field");
+      server.shutdown();
+    }
+  }
+
+  @Test
+  public void testSuperUser() throws Exception {
+    final String collection = "testSuperUser";
+    setupCollectionWithDocSecurity(collection, 1);
+    CloudSolrServer server = getCloudSolrServer(collection);
+    int docCount = 100;
+
+    try {
+      DocLevelGenerator generator = new DocLevelGenerator(collection, 
AUTH_FIELD);
+      generator.generateDocs(server, docCount, "junit_role", "admin_role", 2);
+
+      setAuthenticationUser("solr");
+      String [] ids = new String[docCount];
+      for (int i = 0; i < docCount; ++i) {
+        ids[ i ] = Integer.toString(i);
+      }
+      QueryResponse response = getIdResponse(new ExpectedResult(server, ids, 
docCount));
+      assertEquals("Wrong number of documents", docCount, 
response.getResults().getNumFound());
+    } finally {
+      server.shutdown();
+    }
+  }
+
+  private class AuthFieldModifyThread extends Thread {
+    private CloudSolrServer server;
+    private SolrInputDocument doc;
+    private String authField;
+    private String authFieldValue0;
+    private String authFieldValue1;
+    private volatile boolean finished = false;
+
+    private AuthFieldModifyThread(CloudSolrServer server,
+        SolrInputDocument doc, String authField,
+        String authFieldValue0, String authFieldValue1) {
+      this.server = server;
+      this.doc = doc;
+      this.authField = authField;
+      this.authFieldValue0 = authFieldValue0;
+      this.authFieldValue1 = authFieldValue1;
+    }
+
+    @Override
+    public void run() {
+      while (!finished) {
+        if (rand.nextBoolean()) {
+          doc.setField(authField, authFieldValue0);
+        } else {
+          doc.setField(authField, authFieldValue1);
+        }
+        try {
+          server.add(doc);
+        } catch (SolrServerException sse) {
+          throw new RuntimeException(sse);
+        } catch (IOException ioe) {
+          throw new RuntimeException(ioe);
+        }
+      }
+    }
+
+    public void setFinished() {
+      finished = true;
+    }
+  }
+
+  private static class ExpectedResult {
+    public final CloudSolrServer server;
+    public final String [] ids;
+    public final int expectedDocs;
+    public final String fl;
+
+    public ExpectedResult(CloudSolrServer server, String [] ids, int 
expectedDocs) {
+      this(server, ids, expectedDocs, null);
+    }
+
+    public ExpectedResult(CloudSolrServer server, String [] ids, int 
expectedDocs, String fl) {
+      this.server = server;
+      this.ids = ids;
+      this.expectedDocs = expectedDocs;
+      this.fl = fl;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/59bbfdc7/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/schema.xml
----------------------------------------------------------------------
diff --git 
a/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/schema.xml
 
b/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/schema.xml
index 66449ff..c8bc32f 100644
--- 
a/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/schema.xml
+++ 
b/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/schema.xml
@@ -216,6 +216,7 @@
    <dynamicField name="*_c"   type="currency" indexed="true"  stored="true"/>
 
    <dynamicField name="*_auth" type="string" indexed="true" stored="false" 
multiValued="true"/>
+   <dynamicField name="*_auth_stored" type="string" indexed="true" 
stored="true" multiValued="true"/>
 
    <dynamicField name="ignored_*" type="ignored" multiValued="true"/>
    <dynamicField name="attr_*" type="text_general" indexed="true" 
stored="true" multiValued="true"/>

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/59bbfdc7/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/solrconfig-doclevel.xml
----------------------------------------------------------------------
diff --git 
a/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/solrconfig-doclevel.xml
 
b/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/solrconfig-doclevel.xml
index 4459c0d..f07d494 100644
--- 
a/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/solrconfig-doclevel.xml
+++ 
b/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/solrconfig-doclevel.xml
@@ -387,14 +387,14 @@
          'soft' commit which only ensures that changes are visible
          but does not ensure that data is synced to disk.  This is
          faster and more near-realtime friendly than a hard commit.
-      -->   
-     <autoSoftCommit> 
-       <maxTime>${solr.autoSoftCommit.maxTime:1000}</maxTime> 
+      -->
+     <autoSoftCommit>
+       <maxTime>${solr.autoSoftCommit.maxTime:20000}</maxTime>
      </autoSoftCommit>
-     
+
 
     <!-- Update Related Event Listeners
-         
+
          Various IndexWriter related events can trigger Listeners to
          take actions.
 
@@ -899,7 +899,7 @@
   <!-- realtime get handler, guaranteed to return the latest stored fields of
        any document, without the need to commit or open a new searcher.  The
        current implementation relies on the updateLog feature being enabled. 
-->
-  <requestHandler name="/get" class="solr.RealTimeGetHandler">
+  <requestHandler name="/get" class="solr.SecureRealTimeGetHandler">
      <lst name="defaults">
        <str name="omitHeader">true</str>
        <str name="wt">json</str>
@@ -1351,14 +1351,17 @@
     <bool name="enabled">true</bool>
 
     <!-- Field where the auth tokens are stored in the document -->
-    <str name="sentryAuthField">sentry_auth</str>
+    <str name="sentryAuthField">${sentry.auth.field:sentry_auth}</str>
 
     <!-- Auth token defined to allow any role to access the document.
          Uncomment to enable. -->
     <str name="allRolesToken">OR</str>
   </searchComponent>
 
-  <!-- A request handler for demonstrating the spellcheck component.  
+  <searchComponent name="secureGet" 
class="org.apache.solr.handler.component.SecureRealTimeGetComponent" >
+  </searchComponent>
+
+  <!-- A request handler for demonstrating the spellcheck component.
 
        NOTE: This is purely as an example.  The whole purpose of the
        SpellCheckComponent is to hook it into the request handler that

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/59bbfdc7/sentry-tests/sentry-tests-solr/src/test/resources/solr/sentry/test-authz-provider.ini
----------------------------------------------------------------------
diff --git 
a/sentry-tests/sentry-tests-solr/src/test/resources/solr/sentry/test-authz-provider.ini
 
b/sentry-tests/sentry-tests-solr/src/test/resources/solr/sentry/test-authz-provider.ini
index bccc63e..a376cb8 100644
--- 
a/sentry-tests/sentry-tests-solr/src/test/resources/solr/sentry/test-authz-provider.ini
+++ 
b/sentry-tests/sentry-tests-solr/src/test/resources/solr/sentry/test-authz-provider.ini
@@ -31,7 +31,7 @@ admin_all_group = admin_all_role
 [roles]
 junit_role = collection=admin, collection=collection1, 
collection=docLevelCollection, collection=allRolesCollection, 
collection=testUpdateDeleteOperations
 docLevel_role = collection=docLevelCollection, 
collection=testUpdateDeleteOperations
-admin_role = collection=admin, collection=collection1, 
collection=sentryCollection, collection=sentryCollection_underlying1, 
collection=sentryCollection_underlying2, collection=docLevelCollection, 
collection=allRolesCollection, collection=testInvariantCollection, 
collection=testUpdateDeleteOperations, 
collection=testIndexlevelDoclevelOperations, collection=testUpdateDistribPhase
+admin_role = collection=admin, collection=collection1, 
collection=sentryCollection, collection=sentryCollection_underlying1, 
collection=sentryCollection_underlying2, collection=docLevelCollection, 
collection=allRolesCollection, collection=testInvariantCollection, 
collection=testUpdateDeleteOperations, 
collection=testIndexlevelDoclevelOperations, collection=testUpdateDistribPhase, 
collection=testIdvsIds, collection=testIdvsIdsControl, collection=testFl, 
collection=testNonCommitted, collection=testConcurrentChanges, 
collection=testSuperUser
 sentryCollection_query_role = collection=sentryCollection->action=query
 sentryCollection_update_role = collection=sentryCollection->action=update
 sentryCollection_query_update_role = 
collection=sentryCollection->action=query, 
collection=sentryCollection->action=update

Reply via email to