SENTRY-187: Use invariants rather than default for specification of update index level authorization (Gregory Chanan via Vamsee Yarlagadda)
Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/1d6f38c0 Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/1d6f38c0 Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/1d6f38c0 Branch: refs/heads/master Commit: 1d6f38c081e862d9c2d59638aaf22a3bb27858e5 Parents: b4c39a1 Author: Vamsee <[email protected]> Authored: Tue Apr 29 14:20:30 2014 -0700 Committer: Vamsee <[email protected]> Committed: Tue Apr 29 14:20:30 2014 -0700 ---------------------------------------------------------------------- .../e2e/solr/AbstractSolrSentryTestBase.java | 80 ++++++++++++++++++++ .../tests/e2e/solr/TestDocLevelOperations.java | 56 +------------- .../tests/e2e/solr/TestUpdateOperations.java | 26 +++++++ .../collection1/conf/solrconfig-doclevel.xml | 6 +- .../solr/collection1/conf/solrconfig.xml | 14 +++- .../solr/sentry/test-authz-provider.ini | 2 +- 6 files changed, 128 insertions(+), 56 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/1d6f38c0/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 e90891d..b5b7678 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 @@ -16,16 +16,34 @@ */ package org.apache.sentry.tests.e2e.solr; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.net.MalformedURLException; +import java.net.URI; +import java.util.Collections; import java.util.Comparator; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import com.google.common.io.Files; +import org.apache.commons.io.IOUtils; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.util.EntityUtils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -701,6 +719,68 @@ public class AbstractSolrSentryTestBase { return css; } + /** + * Make a raw http request + */ + protected String makeHttpRequest(CloudSolrServer server, String httpMethod, + String path, byte [] content, String contentType) throws Exception { + HttpClient httpClient = server.getLbServer().getHttpClient(); + Set<String> liveNodes = + server.getZkStateReader().getClusterState().getLiveNodes(); + assertTrue("Expected at least one live node", !liveNodes.isEmpty()); + String firstServer = liveNodes.toArray(new String[0])[0].replace("_solr", "/solr"); + URI uri = new URI("http://" + firstServer + path); + HttpRequestBase method = null; + if ("GET".equals(httpMethod)) { + method = new HttpGet(uri); + } else if ("HEAD".equals(httpMethod)) { + method = new HttpHead(uri); + } else if ("POST".equals(httpMethod)) { + method = new HttpPost(uri); + } else if ("PUT".equals(httpMethod)) { + method = new HttpPut(uri); + } else { + throw new IOException("Unsupported method: " + method); + } + + if (method instanceof HttpEntityEnclosingRequestBase) { + HttpEntityEnclosingRequestBase entityEnclosing = + (HttpEntityEnclosingRequestBase)method; + ByteArrayEntity entityRequest = new ByteArrayEntity(content); + entityRequest.setContentType(contentType); + entityEnclosing.setEntity(entityRequest); + } + + HttpEntity httpEntity = null; + boolean success = false; + String retValue = ""; + try { + final HttpResponse response = httpClient.execute(method); + int httpStatus = response.getStatusLine().getStatusCode(); + httpEntity = response.getEntity(); + + if (httpEntity != null) { + InputStream is = httpEntity.getContent(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + try { + IOUtils.copyLarge(is, os); + os.flush(); + } finally { + IOUtils.closeQuietly(os); + IOUtils.closeQuietly(is); + } + retValue = os.toString(); + } + success = true; + } finally { + if (!success) { + EntityUtils.consumeQuietly(httpEntity); + method.abort(); + } + } + return retValue; + } + protected static void waitForRecoveriesToFinish(String collection, CloudSolrServer solrServer, boolean verbose) throws Exception { http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/1d6f38c0/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 eccc576..2c0914e 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 @@ -24,14 +24,6 @@ import org.junit.Before; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import org.apache.commons.io.IOUtils; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.util.EntityUtils; - import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.impl.CloudSolrServer; import org.apache.solr.client.solrj.response.QueryResponse; @@ -40,12 +32,8 @@ import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrInputDocument; import java.io.File; -import java.io.InputStream; -import java.io.ByteArrayOutputStream; -import java.net.URI; import java.net.URLEncoder; import java.util.ArrayList; -import java.util.Set; import org.junit.Test; @@ -81,44 +69,6 @@ public class TestDocLevelOperations extends AbstractSolrSentryTestBase { setupCollection(name); } - private String doHttpGet(CloudSolrServer server, String path) throws Exception { - HttpClient httpClient = server.getLbServer().getHttpClient(); - Set<String> liveNodes = - server.getZkStateReader().getClusterState().getLiveNodes(); - assertTrue("Expected at least one live node", !liveNodes.isEmpty()); - String firstServer = liveNodes.toArray(new String[0])[0].replace("_solr", "/solr"); - URI uri = new URI("http://" + firstServer + path); - HttpGet get = new HttpGet(uri); - HttpEntity httpEntity = null; - boolean success = false; - String retValue = ""; - try { - final HttpResponse response = httpClient.execute(get); - int httpStatus = response.getStatusLine().getStatusCode(); - httpEntity = response.getEntity(); - - if (httpEntity != null) { - InputStream is = httpEntity.getContent(); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - try { - IOUtils.copyLarge(is, os); - os.flush(); - } finally { - IOUtils.closeQuietly(os); - IOUtils.closeQuietly(is); - } - retValue = os.toString(); - } - success = true; - } finally { - if (!success) { - EntityUtils.consumeQuietly(httpEntity); - get.abort(); - } - } - return retValue; - } - /** * Test that queries from different users only return the documents they have access to. */ @@ -190,20 +140,20 @@ public class TestDocLevelOperations extends AbstractSolrSentryTestBase { setAuthenticationUser("junit"); String fq = URLEncoder.encode(" {!raw f=" + AUTH_FIELD + " v=docLevel}"); String path = "/" + collectionName + "/select?q=*:*&fq="+fq; - String retValue = doHttpGet(server, path); + String retValue = makeHttpRequest(server, "GET", path, null, null); assertTrue(retValue.contains("numFound=\"" + NUM_DOCS / 2 + "\" ")); // test that user can't inject an "OR" into the query final String syntaxErrorMsg = "org.apache.solr.search.SyntaxError: Cannot parse"; fq = URLEncoder.encode(" {!raw f=" + AUTH_FIELD + " v=docLevel} OR "); path = "/" + collectionName + "/select?q=*:*&fq="+fq; - retValue = doHttpGet(server, path); + retValue = makeHttpRequest(server, "GET", path, null, null); assertTrue(retValue.contains(syntaxErrorMsg)); // same test, prefix OR this time fq = URLEncoder.encode(" OR {!raw f=" + AUTH_FIELD + " v=docLevel}"); path = "/" + collectionName + "/select?q=*:*&fq="+fq; - retValue = doHttpGet(server, path); + retValue = makeHttpRequest(server, "GET", path, null, null); assertTrue(retValue.contains(syntaxErrorMsg)); } finally { server.shutdown(); http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/1d6f38c0/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestUpdateOperations.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestUpdateOperations.java b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestUpdateOperations.java index 7163573..b439a91 100644 --- a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestUpdateOperations.java +++ b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/TestUpdateOperations.java @@ -21,6 +21,9 @@ import org.slf4j.LoggerFactory; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.apache.solr.client.solrj.impl.CloudSolrServer; import java.io.File; import java.io.PrintWriter; @@ -85,4 +88,27 @@ public class TestUpdateOperations extends AbstractSolrSentryTestBase { assertEquals("Total test failures: " + testFailures.size() + " \n\n" + testFailures.toString() + "\n\n\n", 0, testFailures.size()); } + + @Test + public void testInvariantProcessor() throws Exception { + String collectionName = "testInvariantCollection"; + // Upload configs to ZK + uploadConfigDirToZk(RESOURCES_DIR + File.separator + DEFAULT_COLLECTION + + File.separator + "conf"); + setupCollection(collectionName); + + // Send a update request and try to set the update.chain to skip the + // index-authorization checks + setAuthenticationUser("junit"); + CloudSolrServer server = getCloudSolrServer(collectionName); + try { + String path = "/" + collectionName + "/update?update.chain=skipUpdateIndexAuthorization&commit=true"; + String body = "<add><doc><field name=\"id\">testInvariantDoc</field></doc></add>"; + String ret = makeHttpRequest(server, "POST", path, body.getBytes("UTF-8"), "text/xml"); + assertTrue("Expected sentry exception", ret.contains("SentrySolrAuthorizationException: User junit" + + " does not have privileges for testInvariantCollection")); + } finally { + server.shutdown(); + } + } } http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/1d6f38c0/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 0ab2278..7c0d73f 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 @@ -1039,7 +1039,7 @@ on each Update Request --> - <lst name="defaults"> + <lst name="invariants"> <str name="update.chain">updateIndexAuthorization</str> </lst> @@ -1050,6 +1050,8 @@ <requestHandler name="/update/json" class="solr.JsonUpdateRequestHandler"> <lst name="defaults"> <str name="stream.contentType">application/json</str> + </lst> + <lst name="invariants"> <str name="update.chain">updateIndexAuthorization</str> </lst> @@ -1057,6 +1059,8 @@ <requestHandler name="/update/csv" class="solr.CSVRequestHandler"> <lst name="defaults"> <str name="stream.contentType">application/csv</str> + </lst> + <lst name="invariants"> <str name="update.chain">updateIndexAuthorization</str> </lst> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/1d6f38c0/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/solrconfig.xml ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/solrconfig.xml b/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/solrconfig.xml index 4276cda..9e71f09 100644 --- a/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/solrconfig.xml +++ b/sentry-tests/sentry-tests-solr/src/test/resources/solr/collection1/conf/solrconfig.xml @@ -1035,7 +1035,7 @@ on each Update Request --> - <lst name="defaults"> + <lst name="invariants"> <str name="update.chain">updateIndexAuthorization</str> </lst> @@ -1046,6 +1046,8 @@ <requestHandler name="/update/json" class="solr.JsonUpdateRequestHandler"> <lst name="defaults"> <str name="stream.contentType">application/json</str> + </lst> + <lst name="invariants"> <str name="update.chain">updateIndexAuthorization</str> </lst> @@ -1053,6 +1055,8 @@ <requestHandler name="/update/csv" class="solr.CSVRequestHandler"> <lst name="defaults"> <str name="stream.contentType">application/csv</str> + </lst> + <lst name="invariants"> <str name="update.chain">updateIndexAuthorization</str> </lst> @@ -1748,6 +1752,14 @@ <processor class="solr.RunUpdateProcessorFactory" /> </updateRequestProcessorChain> + <!-- A request processor chain that skips index level authorization. Check + that clients can't invoke this. --> + <updateRequestProcessorChain name="skipUpdateIndexAuthorization"> + <processor class="solr.LogUpdateProcessorFactory" /> + <processor class="solr.DistributedUpdateProcessorFactory" /> + <processor class="solr.RunUpdateProcessorFactory" /> + </updateRequestProcessorChain> + <!-- Response Writers http://wiki.apache.org/solr/QueryResponseWriter http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/1d6f38c0/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 74176a1..a07fb2d 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=allGroupsCollection docLevel_role = collection=docLevelCollection -admin_role = collection=admin, collection=collection1, collection=sentryCollection, collection=sentryCollection_underlying1, collection=sentryCollection_underlying2, collection=docLevelCollection, collection=allGroupsCollection +admin_role = collection=admin, collection=collection1, collection=sentryCollection, collection=sentryCollection_underlying1, collection=sentryCollection_underlying2, collection=docLevelCollection, collection=allGroupsCollection, collection=testInvariantCollection 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
