You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sentry.apache.org by gq...@apache.org on 2015/02/25 03:09:24 UTC
incubator-sentry git commit: SENTRY-478: Solr Sentry plug-in
integration with DB store (Guoquan Shen, reviewed by Lenni Kuff)
Repository: incubator-sentry
Updated Branches:
refs/heads/master 603ca8998 -> 3893e22dd
SENTRY-478: Solr Sentry plug-in integration with DB store (Guoquan Shen, reviewed by Lenni Kuff)
Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/3893e22d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/3893e22d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/3893e22d
Branch: refs/heads/master
Commit: 3893e22dd879542419fd9da81fe548361f0821db
Parents: 603ca89
Author: Guoquan Shen <gu...@intel.com>
Authored: Wed Feb 25 09:47:17 2015 +0800
Committer: Guoquan Shen <gu...@intel.com>
Committed: Wed Feb 25 09:47:17 2015 +0800
----------------------------------------------------------------------
sentry-binding/sentry-binding-solr/pom.xml | 4 +
.../binding/solr/authz/SolrAuthzBinding.java | 46 ++++
.../core/model/search/SearchConstants.java | 8 +-
.../policy/search/SimpleSearchPolicyEngine.java | 2 +-
.../provider/common/AuthorizationComponent.java | 24 ++
.../thrift/SearchPolicyServiceClient.java | 159 +++++++++++
.../service/thrift/SearchProviderBackend.java | 141 ++++++++++
.../solr/handler/SecureRequestHandlerUtil.java | 12 +
.../handler/admin/SecureCollectionsHandler.java | 10 +
.../SentryIndexAuthorizationSingleton.java | 13 +
sentry-tests/sentry-tests-solr/pom.xml | 5 +
.../e2e/solr/AbstractSolrSentryTestBase.java | 41 ++-
.../AbstractSolrSentryTestWithDbProvider.java | 275 +++++++++++++++++++
.../db/integration/TestSolrAdminOperations.java | 236 ++++++++++++++++
.../integration/TestSolrDocLevelOperations.java | 204 ++++++++++++++
.../db/integration/TestSolrQueryOperations.java | 96 +++++++
.../integration/TestSolrUpdateOperations.java | 105 +++++++
17 files changed, 1356 insertions(+), 25 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-binding/sentry-binding-solr/pom.xml
----------------------------------------------------------------------
diff --git a/sentry-binding/sentry-binding-solr/pom.xml b/sentry-binding/sentry-binding-solr/pom.xml
index b3329e2..4e785e7 100644
--- a/sentry-binding/sentry-binding-solr/pom.xml
+++ b/sentry-binding/sentry-binding-solr/pom.xml
@@ -48,6 +48,10 @@ limitations under the License.
</dependency>
<dependency>
<groupId>org.apache.sentry</groupId>
+ <artifactId>sentry-provider-db</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sentry</groupId>
<artifactId>sentry-provider-file</artifactId>
<scope>test</scope>
</dependency>
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-binding/sentry-binding-solr/src/main/java/org/apache/sentry/binding/solr/authz/SolrAuthzBinding.java
----------------------------------------------------------------------
diff --git a/sentry-binding/sentry-binding-solr/src/main/java/org/apache/sentry/binding/solr/authz/SolrAuthzBinding.java b/sentry-binding/sentry-binding-solr/src/main/java/org/apache/sentry/binding/solr/authz/SolrAuthzBinding.java
index a43a1e0..373ee8c 100644
--- a/sentry-binding/sentry-binding-solr/src/main/java/org/apache/sentry/binding/solr/authz/SolrAuthzBinding.java
+++ b/sentry-binding/sentry-binding-solr/src/main/java/org/apache/sentry/binding/solr/authz/SolrAuthzBinding.java
@@ -26,6 +26,7 @@ import org.apache.hadoop.conf.Configuration;
import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.sentry.SentryUserException;
import org.apache.sentry.binding.solr.conf.SolrAuthzConf;
import org.apache.sentry.binding.solr.conf.SolrAuthzConf.AuthzConfVars;
import org.apache.sentry.core.common.ActiveRoleSet;
@@ -36,6 +37,8 @@ import org.apache.sentry.policy.common.PolicyEngine;
import org.apache.sentry.provider.common.AuthorizationProvider;
import org.apache.sentry.provider.common.GroupMappingService;
import org.apache.sentry.provider.common.ProviderBackend;
+import org.apache.sentry.provider.db.generic.service.thrift.SearchPolicyServiceClient;
+import org.apache.sentry.provider.db.generic.service.thrift.SearchProviderBackend;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -58,11 +61,17 @@ public class SolrAuthzBinding {
private final AuthorizationProvider authProvider;
private final GroupMappingService groupMapping;
private ProviderBackend providerBackend;
+ private Subject bindingSubject;
public SolrAuthzBinding (SolrAuthzConf authzConf) throws Exception {
this.authzConf = addHdfsPropsToConf(authzConf);
this.authProvider = getAuthProvider();
this.groupMapping = authProvider.getGroupMapping();
+ /**
+ * The Solr server principal will use the binding
+ */
+ this.bindingSubject = new Subject(UserGroupInformation.getCurrentUser()
+ .getShortUserName());
}
// Instantiate the configured authz provider
@@ -206,4 +215,41 @@ public class SolrAuthzBinding {
}
}
}
+
+ /**
+ * SENTRY-478
+ * If the binding uses the searchProviderBackend, it can sync privilege with Sentry Service
+ */
+ public boolean isSyncEnabled() {
+ return (providerBackend instanceof SearchProviderBackend);
+ }
+
+ public SearchPolicyServiceClient getClient() throws Exception {
+ return new SearchPolicyServiceClient(authzConf);
+ }
+
+ /**
+ * Attempt to notify the Sentry service when deleting collection happened
+ * @param collection
+ * @throws SolrException
+ */
+ public void deleteCollectionPrivilege(String collection) throws SentrySolrAuthorizationException {
+ if (!isSyncEnabled()) {
+ return;
+ }
+ SearchPolicyServiceClient client = null;
+ try {
+ client = getClient();
+ client.dropCollectionPrivilege(collection, bindingSubject.getName());
+ } catch (SentryUserException ex) {
+ throw new SentrySolrAuthorizationException("User " + bindingSubject.getName() +
+ " can't delete privileges for collection " + collection);
+ } catch (Exception ex) {
+ throw new SentrySolrAuthorizationException("Unable to obtain client:" + ex.getMessage());
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-core/sentry-core-model-search/src/main/java/org/apache/sentry/core/model/search/SearchConstants.java
----------------------------------------------------------------------
diff --git a/sentry-core/sentry-core-model-search/src/main/java/org/apache/sentry/core/model/search/SearchConstants.java b/sentry-core/sentry-core-model-search/src/main/java/org/apache/sentry/core/model/search/SearchConstants.java
index 7a39b79..16b9195 100644
--- a/sentry-core/sentry-core-model-search/src/main/java/org/apache/sentry/core/model/search/SearchConstants.java
+++ b/sentry-core/sentry-core-model-search/src/main/java/org/apache/sentry/core/model/search/SearchConstants.java
@@ -21,5 +21,11 @@ public class SearchConstants {
public static final String ALL = "*";
public static final String QUERY = "query";
public static final String UPDATE = "update";
-
+ /**
+ * The property of sentry.search.cluster was used to distinguish itself from multiple search clusters. For example, there are two
+ * search clusters: cluster1 and cluster2 implemented authorization via sentry, and it must set the value of
+ * sentry.search.cluster=cluster1 or cluster2 to communicate with sentry service for authorization
+ */
+ public static final String SENTRY_SEARCH_CLUSTER_KEY = "sentry.search.cluster";
+ public static final String SENTRY_SEARCH_CLUSTER_DEFAULT = "clutser1";
}
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-policy/sentry-policy-search/src/main/java/org/apache/sentry/policy/search/SimpleSearchPolicyEngine.java
----------------------------------------------------------------------
diff --git a/sentry-policy/sentry-policy-search/src/main/java/org/apache/sentry/policy/search/SimpleSearchPolicyEngine.java b/sentry-policy/sentry-policy-search/src/main/java/org/apache/sentry/policy/search/SimpleSearchPolicyEngine.java
index f428aea..bc518fb 100644
--- a/sentry-policy/sentry-policy-search/src/main/java/org/apache/sentry/policy/search/SimpleSearchPolicyEngine.java
+++ b/sentry-policy/sentry-policy-search/src/main/java/org/apache/sentry/policy/search/SimpleSearchPolicyEngine.java
@@ -85,7 +85,7 @@ public class SimpleSearchPolicyEngine implements PolicyEngine {
@Override
public void validatePolicy(boolean strictValidation)
throws SentryConfigurationException {
- throw new SentryConfigurationException("Not implemented yet");
+ providerBackend.validatePolicy(strictValidation);
}
public static ImmutableList<PrivilegeValidator> createPrivilegeValidators() {
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/AuthorizationComponent.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/AuthorizationComponent.java b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/AuthorizationComponent.java
new file mode 100644
index 0000000..def3486
--- /dev/null
+++ b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/AuthorizationComponent.java
@@ -0,0 +1,24 @@
+/*
+ * 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.provider.common;
+/**
+ * Represent which component being authorized by Sentry
+ * using generic model
+ */
+public class AuthorizationComponent{
+ public static final String Search = "solr";
+}
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SearchPolicyServiceClient.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SearchPolicyServiceClient.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SearchPolicyServiceClient.java
new file mode 100644
index 0000000..1ed3fcd
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SearchPolicyServiceClient.java
@@ -0,0 +1,159 @@
+/**
+ * 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.provider.db.generic.service.thrift;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.SentryUserException;
+import org.apache.sentry.core.common.Action;
+import org.apache.sentry.core.common.ActiveRoleSet;
+import org.apache.sentry.core.common.Authorizable;
+import org.apache.sentry.core.model.search.Collection;
+import org.apache.sentry.provider.common.AuthorizationComponent;
+
+import com.google.common.collect.Lists;
+
+import static org.apache.sentry.core.model.search.SearchModelAuthorizable.AuthorizableType.Collection;
+import static org.apache.sentry.core.model.search.SearchConstants.SENTRY_SEARCH_CLUSTER_KEY;
+import static org.apache.sentry.core.model.search.SearchConstants.SENTRY_SEARCH_CLUSTER_DEFAULT;
+
+/**
+ * This search policy client will be used in the solr component to communicate with Sentry service.
+ *
+ */
+public class SearchPolicyServiceClient {
+ private static final String COMPONENT_TYPE = AuthorizationComponent.Search;
+
+ private String searchClusterName;
+ private SentryGenericServiceClient client;
+
+ public SearchPolicyServiceClient(Configuration conf) throws Exception {
+ this.searchClusterName = conf.get(SENTRY_SEARCH_CLUSTER_KEY, SENTRY_SEARCH_CLUSTER_DEFAULT);
+ this.client = new SentryGenericServiceClient(conf);
+ }
+
+ public void createRole(final String requestor, final String roleName)
+ throws SentryUserException {
+ client.createRole(requestor, roleName, COMPONENT_TYPE);
+ }
+
+ public void createRoleIfNotExist(final String requestor,
+ final String roleName) throws SentryUserException {
+ client.createRoleIfNotExist(requestor, roleName, COMPONENT_TYPE);
+ }
+
+ public void dropRole(final String requestor, final String roleName)
+ throws SentryUserException {
+ client.dropRole(requestor, roleName, COMPONENT_TYPE);
+ }
+
+ public void dropRoleIfExists(final String requestor, final String roleName)
+ throws SentryUserException {
+ client.dropRoleIfExists(requestor, roleName, COMPONENT_TYPE);
+ }
+
+ public void addRoleToGroups(final String requestor, final String roleName,
+ final Set<String> groups) throws SentryUserException {
+ client.addRoleToGroups(requestor, roleName, COMPONENT_TYPE, groups);
+ }
+
+ public void deleteRoleFromGroups(final String requestor, final String roleName,
+ final Set<String> groups) throws SentryUserException {
+ client.deleteRoleToGroups(requestor, roleName, COMPONENT_TYPE, groups);
+ }
+
+ public void grantCollectionPrivilege(final String collection, final String requestor,
+ final String roleName,final String action) throws SentryUserException {
+ grantCollectionPrivilege(collection, requestor, roleName, action, false);
+ }
+
+ public void grantCollectionPrivilege(final String collection, final String requestor,
+ final String roleName, final String action, final Boolean grantOption) throws SentryUserException {
+ TSentryPrivilege tPrivilege = toTSentryPrivilege(collection, action, grantOption);
+ client.grantPrivilege(requestor, roleName, COMPONENT_TYPE, tPrivilege);
+ }
+
+ public void revokeCollectionPrivilege(final String collection, final String requestor, final String roleName,
+ final String action) throws SentryUserException {
+ revokeCollectionPrivilege(collection, requestor, roleName, action, false);
+ }
+
+ public void revokeCollectionPrivilege(final String collection, final String requestor, final String roleName,
+ final String action, final Boolean grantOption) throws SentryUserException {
+ TSentryPrivilege tPrivilege = toTSentryPrivilege(collection, action, grantOption);
+ client.revokePrivilege(requestor, roleName, COMPONENT_TYPE, tPrivilege);
+ }
+
+ public void renameCollectionPrivilege(final String oldCollection, final String newCollection, final String requestor)
+ throws SentryUserException {
+ client.renamePrivilege(requestor, COMPONENT_TYPE, searchClusterName, Lists.newArrayList(new Collection(oldCollection)),
+ Lists.newArrayList(new Collection(newCollection)));
+ }
+
+ public void dropCollectionPrivilege(final String collection, final String requestor) throws SentryUserException {
+ final TSentryPrivilege tPrivilege = toTSentryPrivilege(collection, Action.ALL, null);
+ client.dropPrivilege(requestor, COMPONENT_TYPE, tPrivilege);
+ }
+
+ public Set<TSentryRole> listAllRoles(final String user) throws SentryUserException {
+ return client.listAllRoles(user, COMPONENT_TYPE);
+ }
+
+ public Set<TSentryRole> listRolesByGroupName(final String requestor, final String groupName) throws SentryUserException {
+ return client.listRolesByGroupName(requestor, groupName, COMPONENT_TYPE);
+ }
+
+ public Set<TSentryPrivilege> listPrivilegesByRoleName(
+ final String requestor, final String roleName,
+ final List<? extends Authorizable> authorizables) throws SentryUserException {
+ return client.listPrivilegesByRoleName(requestor, roleName, COMPONENT_TYPE, searchClusterName, authorizables);
+ }
+
+ public Set<String> listPrivilegesForProvider(final ActiveRoleSet roleSet, final Set<String> groups,
+ final List<? extends Authorizable> authorizables) throws SentryUserException {
+ return client.listPrivilegesForProvider(COMPONENT_TYPE, searchClusterName, roleSet, groups, authorizables);
+ }
+
+ private TSentryPrivilege toTSentryPrivilege(String collection, String action,
+ Boolean grantOption) {
+ TSentryPrivilege tPrivilege = new TSentryPrivilege();
+ tPrivilege.setComponent(COMPONENT_TYPE);
+ tPrivilege.setServiceName(searchClusterName);
+ tPrivilege.setAction(action);
+
+ if (grantOption == null) {
+ tPrivilege.setGrantOption(TSentryGrantOption.UNSET);
+ } else if (grantOption) {
+ tPrivilege.setGrantOption(TSentryGrantOption.TRUE);
+ } else {
+ tPrivilege.setGrantOption(TSentryGrantOption.FALSE);
+ }
+
+ List<TAuthorizable> authorizables = Lists.newArrayList(new TAuthorizable(Collection.name(), collection));
+ tPrivilege.setAuthorizables(authorizables);
+ return tPrivilege;
+ }
+
+ public void close() {
+ if (client != null) {
+ client.close();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SearchProviderBackend.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SearchProviderBackend.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SearchProviderBackend.java
new file mode 100644
index 0000000..ae324bf
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/thrift/SearchProviderBackend.java
@@ -0,0 +1,141 @@
+/**
+ * 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.provider.db.generic.service.thrift;
+
+import java.util.Arrays;
+import java.util.Set;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.sentry.SentryUserException;
+import org.apache.sentry.core.common.ActiveRoleSet;
+import org.apache.sentry.core.common.Authorizable;
+import org.apache.sentry.core.common.SentryConfigurationException;
+import org.apache.sentry.core.common.Subject;
+import org.apache.sentry.provider.common.ProviderBackend;
+import org.apache.sentry.provider.common.ProviderBackendContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+/**
+ * when Solr integration with Database store, this backend will communicate with Sentry service to get
+ * privileges according to the requested groups
+ *
+ */
+public class SearchProviderBackend implements ProviderBackend {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SearchProviderBackend.class);
+ private final Configuration conf;
+ private final Subject subject;
+ private volatile boolean initialized = false;
+
+ public SearchProviderBackend(Configuration conf, String resourcePath) throws Exception {
+ this.conf = conf;
+ /**
+ * Who create the searchProviderBackend, this subject will been used the requester to communicate
+ * with Sentry Service
+ */
+ subject = new Subject(UserGroupInformation.getCurrentUser()
+ .getShortUserName());
+ }
+
+ @Override
+ public void initialize(ProviderBackendContext context) {
+ if (initialized) {
+ throw new IllegalStateException("SearchProviderBackend has already been initialized, cannot be initialized twice");
+ }
+ this.initialized = true;
+ }
+
+ @Override
+ public ImmutableSet<String> getPrivileges(Set<String> groups,
+ ActiveRoleSet roleSet, Authorizable... authorizableHierarchy) {
+ if (!initialized) {
+ throw new IllegalStateException("SearchProviderBackend has not been properly initialized");
+ }
+ SearchPolicyServiceClient client = null;
+ try {
+ client = getClient();
+ return ImmutableSet.copyOf(client.listPrivilegesForProvider(roleSet, groups, Arrays.asList(authorizableHierarchy)));
+ } catch (SentryUserException e) {
+ String msg = "Unable to obtain privileges from server: " + e.getMessage();
+ LOGGER.error(msg, e);
+ } catch (Exception e) {
+ String msg = "Unable to obtain client:" + e.getMessage();
+ LOGGER.error(msg, e);
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ }
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public ImmutableSet<String> getRoles(Set<String> groups, ActiveRoleSet roleSet) {
+ if (!initialized) {
+ throw new IllegalStateException("SearchProviderBackend has not been properly initialized");
+ }
+ SearchPolicyServiceClient client = null;
+ try {
+ Set<TSentryRole> tRoles = Sets.newHashSet();
+ client = getClient();
+ //get the roles according to group
+ for (String group : groups) {
+ tRoles.addAll(client.listRolesByGroupName(subject.getName(), group));
+ }
+ Set<String> roles = Sets.newHashSet();
+ for (TSentryRole tRole : tRoles) {
+ roles.add(tRole.getRoleName());
+ }
+ return ImmutableSet.copyOf(roleSet.isAll() ? roles : Sets.intersection(roles, roleSet.getRoles()));
+ } catch (SentryUserException e) {
+ String msg = "Unable to obtain roles from server: " + e.getMessage();
+ LOGGER.error(msg, e);
+ } catch (Exception e) {
+ String msg = "Unable to obtain client:" + e.getMessage();
+ LOGGER.error(msg, e);
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ }
+ return ImmutableSet.of();
+ }
+
+ public SearchPolicyServiceClient getClient() throws Exception {
+ return new SearchPolicyServiceClient(conf);
+ }
+
+ /**
+ * SearchProviderBackend does nothing in the validatePolicy()
+ */
+ @Override
+ public void validatePolicy(boolean strictValidation)
+ throws SentryConfigurationException {
+ if (!initialized) {
+ throw new IllegalStateException("Backend has not been properly initialized");
+ }
+ }
+
+ @Override
+ public void close() {
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRequestHandlerUtil.java
----------------------------------------------------------------------
diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRequestHandlerUtil.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRequestHandlerUtil.java
index 8b46388..7ae5391 100644
--- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRequestHandlerUtil.java
+++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRequestHandlerUtil.java
@@ -55,6 +55,18 @@ public class SecureRequestHandlerUtil {
checkSentry(req, andActions, false, false, null);
}
+ /**
+ * Attempt to sync collection privileges with Sentry when the metadata has changed.
+ * ex: When the collection has been deleted, the privileges related to the collection
+ * were also needed to drop. When the collection has been renamed, the privileges must been
+ * renamed too.
+ */
+ public static void syncDeleteCollection(String collection) {
+ final SentryIndexAuthorizationSingleton sentryInstance =
+ (testOverride == null)?SentryIndexAuthorizationSingleton.getInstance():testOverride;
+ sentryInstance.deleteCollection(collection);
+ }
+
private static void checkSentry(SolrQueryRequest req, Set<SearchModelAction> andActions,
boolean admin, boolean checkCollection, String collection) {
// Sentry currently does have AND support for actions; need to check
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java
----------------------------------------------------------------------
diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java
index 20d9626..0a471a4 100644
--- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java
+++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java
@@ -77,5 +77,15 @@ public class SecureCollectionsHandler extends CollectionsHandler {
SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.UPDATE_ONLY,
true, collection);
super.handleRequestBody(req, rsp);
+
+ /**
+ * Attempt to sync collection privileges with Sentry when the metadata has changed.
+ * ex: When the collection has been deleted, the privileges related to the collection
+ * were also needed to drop.
+ */
+ if (action.equals(CollectionAction.DELETE)) {
+ SecureRequestHandlerUtil.syncDeleteCollection(collection);
+ }
+
}
}
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java
----------------------------------------------------------------------
diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java
index d44b228..53c8946 100644
--- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java
+++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java
@@ -190,4 +190,17 @@ public class SentryIndexAuthorizationSingleton {
return req instanceof LocalSolrQueryRequest?
superUser:(String)httpServletRequest.getAttribute(USER_NAME);
}
+
+ /**
+ * Attempt to notify the Sentry service when deleting collection happened
+ * @param collection
+ * @throws SolrException
+ */
+ public void deleteCollection(String collection) throws SolrException {
+ try {
+ binding.deleteCollectionPrivilege(collection);
+ } catch (SentrySolrAuthorizationException ex) {
+ throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, ex);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-tests/sentry-tests-solr/pom.xml
----------------------------------------------------------------------
diff --git a/sentry-tests/sentry-tests-solr/pom.xml b/sentry-tests/sentry-tests-solr/pom.xml
index 12fea7c..5a1e5c2 100644
--- a/sentry-tests/sentry-tests-solr/pom.xml
+++ b/sentry-tests/sentry-tests-solr/pom.xml
@@ -44,6 +44,11 @@ limitations under the License.
<artifactId>solr-sentry-handlers</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.sentry</groupId>
+ <artifactId>sentry-provider-db</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
</dependency>
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/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 ea2b12f..2495a9e 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,13 +16,17 @@
*/
package org.apache.sentry.tests.e2e.solr;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
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;
@@ -30,9 +34,11 @@ import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
-import com.google.common.io.Files;
+import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
-
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
@@ -44,33 +50,20 @@ 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;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.hadoop.fs.FileSystem;
-import org.apache.hadoop.fs.Path;
-import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.sentry.binding.solr.HdfsTestUtil;
import org.apache.solr.client.solrj.SolrQuery;
-import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.impl.CloudSolrServer;
-import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.util.ClientUtils;
-import org.apache.solr.cloud.AbstractFullDistribZkTestBase;
-import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.cloud.MiniSolrCloudCluster;
import org.apache.solr.cloud.ZkController;
-import org.apache.solr.common.cloud.Replica;
-import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionParams.CollectionAction;
import org.apache.solr.common.params.CoreAdminParams;
@@ -84,18 +77,20 @@ import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.io.Files;
+
public class AbstractSolrSentryTestBase {
private static final Logger LOG = LoggerFactory.getLogger(AbstractSolrSentryTestBase.class);
protected static final String SENTRY_ERROR_MSG = "SentrySolrAuthorizationException";
- private static MiniDFSCluster dfsCluster;
- private static MiniSolrCloudCluster miniSolrCloudCluster;
- private static SortedMap<Class, String> extraRequestFilters;
+ protected static MiniDFSCluster dfsCluster;
+ protected static MiniSolrCloudCluster miniSolrCloudCluster;
+ protected static SortedMap<Class, String> extraRequestFilters;
protected static final String ADMIN_USER = "admin";
protected static final String ALL_DOCS = "*:*";
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";
- private static final int NUM_SERVERS = 4;
+ protected static final int NUM_SERVERS = 4;
private static void addPropertyToSentry(StringBuilder builder, String name, String value) {
builder.append("<property>\n");
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/AbstractSolrSentryTestWithDbProvider.java
----------------------------------------------------------------------
diff --git a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/AbstractSolrSentryTestWithDbProvider.java b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/AbstractSolrSentryTestWithDbProvider.java
new file mode 100644
index 0000000..9438ee5
--- /dev/null
+++ b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/AbstractSolrSentryTestWithDbProvider.java
@@ -0,0 +1,275 @@
+/*
+ * 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.db.integration;
+
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Comparator;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.CommonConfigurationKeys;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.net.NetUtils;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.sentry.binding.solr.HdfsTestUtil;
+import org.apache.sentry.binding.solr.conf.SolrAuthzConf.AuthzConfVars;
+import org.apache.sentry.core.model.search.SearchConstants;
+import org.apache.sentry.provider.common.AuthorizationComponent;
+import org.apache.sentry.provider.db.generic.service.thrift.SearchPolicyServiceClient;
+import org.apache.sentry.provider.db.generic.service.thrift.SearchProviderBackend;
+import org.apache.sentry.provider.file.LocalGroupResourceAuthorizationProvider;
+import org.apache.sentry.provider.file.PolicyFile;
+import org.apache.sentry.service.thrift.SentryService;
+import org.apache.sentry.service.thrift.SentryServiceFactory;
+import org.apache.sentry.service.thrift.ServiceConstants.ClientConfig;
+import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
+import org.apache.sentry.tests.e2e.solr.AbstractSolrSentryTestBase;
+import org.apache.sentry.tests.e2e.solr.ModifiableUserAuthenticationFilter;
+import org.apache.solr.cloud.MiniSolrCloudCluster;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Sets;
+
+/**
+ * This class used to test the Solr integration with DB store.
+ * It will set up a miniSolrCloud, miniHDFS and Sentry service in a JVM process.
+ */
+public class AbstractSolrSentryTestWithDbProvider extends AbstractSolrSentryTestBase{
+ private static final Logger LOGGER = LoggerFactory
+ .getLogger(AbstractSolrSentryTestWithDbProvider.class);
+
+ protected static final String SERVER_HOST = NetUtils
+ .createSocketAddr("localhost:80").getAddress().getCanonicalHostName();
+ protected static final int PORT = 8038;
+ protected static final String ADMIN_GROUP = "admin_group";
+ protected static final String ADMIN_ROLE = "admin_role";
+ protected static final String ADMIN_COLLECTION_NAME = "admin";
+
+ protected static final Configuration conf = new Configuration(false);
+
+ protected static SentryService server;
+ protected static SearchPolicyServiceClient client;
+
+ protected static File baseDir;
+ protected static File hdfsDir;
+ protected static File dbDir;
+ protected static File policyFilePath;
+ protected static File sentrySitePath;
+
+ protected static PolicyFile policyFile;
+
+ /**
+ * Overwrite the method from super class AbstractSolrSentryTestBase
+ * take over the management of miniSolrCloudCluster and dfsCluster
+ */
+ @BeforeClass
+ public static void beforeTestSimpleSolrEndToEnd() throws Exception {
+ setupConf();
+ startHDFS();
+ startSolrWithDbProvider();
+ startSentryService();
+ connectToSentryService();
+ setGroupsAndRoles();
+ }
+
+ @AfterClass
+ public static void teardownClass() throws Exception {
+ stopAllService();
+ FileUtils.deleteDirectory(baseDir);
+ unsetSystemProperties();
+ }
+
+ public static void setupConf() throws Exception {
+ baseDir = createTempDir();
+ hdfsDir = new File(baseDir, "hdfs");
+ dbDir = new File(baseDir, "sentry_policy_db");
+ policyFilePath = new File(baseDir, "local_policy_file.ini");
+ sentrySitePath = new File(baseDir, "sentry-site.xml");
+ policyFile = new PolicyFile();
+
+ conf.set(ServerConfig.SECURITY_MODE, ServerConfig.SECURITY_MODE_NONE);
+ conf.set(ServerConfig.SENTRY_VERIFY_SCHEM_VERSION, "false");
+ conf.set(ServerConfig.ADMIN_GROUPS, ADMIN_GROUP + ",solr");
+ conf.set(ServerConfig.RPC_ADDRESS, SERVER_HOST);
+ conf.set(ServerConfig.RPC_PORT, String.valueOf(PORT));
+ conf.set(ServerConfig.SENTRY_STORE_JDBC_URL,
+ "jdbc:derby:;databaseName=" + dbDir.getPath() + ";create=true");
+ conf.set(ServerConfig.SENTRY_STORE_GROUP_MAPPING_RESOURCE,
+ policyFilePath.getPath());
+ server = new SentryServiceFactory().create(conf);
+
+ conf.set(ClientConfig.SERVER_RPC_ADDRESS, server.getAddress().getHostName());
+ conf.set(ClientConfig.SERVER_RPC_PORT, String.valueOf(server.getAddress().getPort()));
+ conf.set(ServerConfig.SENTRY_STORE_GROUP_MAPPING,
+ ServerConfig.SENTRY_STORE_LOCAL_GROUP_MAPPING);
+ conf.set(AuthzConfVars.AUTHZ_PROVIDER.getVar(),
+ LocalGroupResourceAuthorizationProvider.class.getName());
+ conf.set(AuthzConfVars.AUTHZ_PROVIDER_BACKEND.getVar(), SearchProviderBackend.class.getName());
+ conf.set(AuthzConfVars.AUTHZ_PROVIDER_RESOURCE.getVar(), policyFilePath.getPath());
+ }
+
+ public static File createTempDir() {
+ File baseDir = new File(System.getProperty("java.io.tmpdir"));
+ String baseName = "solr-integration-db-";
+ File tempDir = new File(baseDir, baseName + UUID.randomUUID().toString());
+ if (tempDir.mkdir()) {
+ return tempDir;
+ }
+ throw new IllegalStateException("Failed to create temp directory");
+ }
+
+ public static void configureWithSolr() throws Exception {
+ conf.set(ServerConfig.SECURITY_USE_UGI_TRANSPORT, "true");
+ //save configuration to sentry-site.xml
+ conf.writeXml(new FileOutputStream(sentrySitePath));
+ setSystemProperties();
+ extraRequestFilters = new TreeMap<Class, String>(new Comparator<Class>() {
+ // There's only one class, make this as simple as possible
+ @Override
+ public int compare(Class o1, Class o2) {
+ return 0;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ return true;
+ }
+ });
+ extraRequestFilters.put(ModifiableUserAuthenticationFilter.class, "*");
+
+ //set the solr for the loginUser and belongs to solr group
+ addGroupsToUser("solr", "solr");
+ UserGroupInformation.setLoginUser(UserGroupInformation.createUserForTesting("solr", new String[]{"solr"}));
+ }
+
+ public static void startHDFS() throws Exception {
+ dfsCluster = HdfsTestUtil.setupClass(hdfsDir.getPath());
+ conf.set(
+ CommonConfigurationKeys.FS_DEFAULT_NAME_KEY,
+ dfsCluster.getFileSystem().getConf()
+ .get(CommonConfigurationKeys.FS_DEFAULT_NAME_KEY));
+ }
+
+ public static void startSolrWithDbProvider() throws Exception {
+ LOGGER.info("starting Solr authorization via Sentry Service");
+ configureWithSolr();
+ miniSolrCloudCluster = new MiniSolrCloudCluster(NUM_SERVERS, null,
+ new File(RESOURCES_DIR, "solr-no-core.xml"), null, extraRequestFilters);
+ }
+
+ public static void startSentryService() throws Exception {
+ server.start();
+ final long start = System.currentTimeMillis();
+ while(!server.isRunning()) {
+ Thread.sleep(1000);
+ if(System.currentTimeMillis() - start > 60000L) {
+ throw new TimeoutException("Server did not start after 60 seconds");
+ }
+ }
+ }
+
+ public static void connectToSentryService() throws Exception {
+ client = new SearchPolicyServiceClient(conf);
+ }
+
+ public static void stopAllService() throws Exception {
+ if (miniSolrCloudCluster != null) {
+ miniSolrCloudCluster.shutdown();
+ miniSolrCloudCluster = null;
+ }
+ if (dfsCluster != null) {
+ HdfsTestUtil.teardownClass(dfsCluster);
+ dfsCluster = null;
+ }
+ if (client != null) {
+ client.close();
+ client = null;
+ }
+ if (server != null) {
+ server.stop();
+ server = null;
+ }
+ }
+
+ public static void addGroupsToUser(String user, String... groupNames) {
+ policyFile.addGroupsToUser(user, groupNames);
+ }
+
+ public static void writePolicyFile() throws Exception {
+ policyFile.write(policyFilePath);
+ FileSystem clusterFs = dfsCluster.getFileSystem();
+ clusterFs.copyFromLocalFile(false,
+ new Path(policyFilePath.getPath()),
+ new Path(policyFilePath.getPath()));
+ }
+
+ public static void setSystemProperties() throws Exception {
+ System.setProperty("solr.xml.persist", "true");
+ // Disable the block cache because we can run out of memory
+ // on a MiniCluster.
+ System.setProperty("solr.hdfs.blockcache.enabled", "false");
+ System.setProperty("solr.hdfs.home", dfsCluster.getURI().toString() + "/solr");
+ System.setProperty("solr.authorization.sentry.site", sentrySitePath.toURI().toURL().toString().substring("file:".length()));
+ }
+
+ public static void unsetSystemProperties() {
+ System.clearProperty("solr.xml.persist");
+ System.clearProperty("solr.hdfs.blockcache.enabled");
+ System.clearProperty("solr.hdfs.home");
+ System.clearProperty("solr.authorization.sentry.site");
+ }
+
+ public static void setGroupsAndRoles() throws Exception {
+ /**set local group mapping
+ * user0->group0->role0
+ * user1->group1->role1
+ * user2->group2->role2
+ * user3->group3->role3
+ */
+ String[] users = {"user0","user1","user2","user3"};
+ String[] groups = {"group0","group1","group2","group3"};
+ String[] roles = {"role0","role1","role2","role3"};
+
+ for (int i = 0; i < users.length; i++) {
+ addGroupsToUser(users[i], groups[i]);
+ }
+ addGroupsToUser(ADMIN_USER, ADMIN_GROUP);
+ writePolicyFile();
+
+ for (int i = 0; i < roles.length; i++) {
+ client.createRole(ADMIN_USER, roles[i]);
+ client.addRoleToGroups(ADMIN_USER, roles[i], Sets.newHashSet(groups[i]));
+ }
+
+ /**
+ * user[admin]->group[admin]->role[admin]
+ * grant ALL privilege on collection ALL to role admin
+ */
+ client.createRole(ADMIN_USER, ADMIN_ROLE);
+ client.addRoleToGroups(ADMIN_USER, ADMIN_ROLE, Sets.newHashSet(ADMIN_GROUP));
+ client.grantCollectionPrivilege(SearchConstants.ALL, ADMIN_USER, ADMIN_ROLE, SearchConstants.ALL);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrAdminOperations.java
----------------------------------------------------------------------
diff --git a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrAdminOperations.java b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrAdminOperations.java
new file mode 100644
index 0000000..00a7a89
--- /dev/null
+++ b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrAdminOperations.java
@@ -0,0 +1,236 @@
+/*
+ * 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.db.integration;
+
+
+import java.io.File;
+import java.util.Arrays;
+
+import org.apache.sentry.core.model.search.Collection;
+import org.apache.sentry.core.model.search.SearchConstants;
+import org.apache.solr.common.params.CollectionParams.CollectionAction;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.assertTrue;
+
+public class TestSolrAdminOperations extends AbstractSolrSentryTestWithDbProvider {
+ private static final Logger LOG = LoggerFactory.getLogger(TestSolrAdminOperations.class);
+ private static final String TEST_COLLECTION_NAME1 = "collection1";
+ private static final String COLLECTION_CONFIG_DIR = RESOURCES_DIR + File.separator + "collection1" + File.separator + "conf";
+
+ @Test
+ public void testAdminOperations() throws Exception {
+ /**
+ * Upload configs to ZK for create collection
+ */
+ uploadConfigDirToZk(COLLECTION_CONFIG_DIR);
+
+ /**
+ * verify admin user has all privileges
+ */
+ verifyCollectionAdminOpPass(ADMIN_USER, CollectionAction.CREATE, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(ADMIN_USER, CollectionAction.DELETE, TEST_COLLECTION_NAME1);
+
+ String grantor = "user0";
+ /**
+ * user0->group0->role0
+ * grant ALL privilege on collection admin and collection1 to role0
+ */
+ client.grantCollectionPrivilege(ADMIN_COLLECTION_NAME, ADMIN_USER, "role0", SearchConstants.ALL);
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role0", SearchConstants.ALL);
+
+ verifyCollectionAdminOpPass(grantor, CollectionAction.CREATE, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.RELOAD, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.CREATEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.DELETEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.DELETE, TEST_COLLECTION_NAME1);
+
+ //revoke UPDATE privilege on collection collection1 from role1, create collection1 will be failed
+ client.revokeCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role0", SearchConstants.UPDATE);
+
+ verifyCollectionAdminOpFail(grantor, CollectionAction.CREATE, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.RELOAD, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.CREATEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.DELETEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.DELETE, TEST_COLLECTION_NAME1);
+
+ /**
+ * user1->group1->role1
+ * grant UPDATE privilege on collection admin and collection1 to role1
+ */
+ grantor = "user1";
+ client.grantCollectionPrivilege(ADMIN_COLLECTION_NAME, ADMIN_USER, "role1", SearchConstants.UPDATE);
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role1", SearchConstants.UPDATE);
+
+ verifyCollectionAdminOpPass(grantor, CollectionAction.CREATE, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.RELOAD, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.CREATEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.DELETEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.DELETE, TEST_COLLECTION_NAME1);
+
+ //revoke UPDATE privilege on collection admin from role1, create collection1 will be failed
+ client.revokeCollectionPrivilege(ADMIN_COLLECTION_NAME, ADMIN_USER, "role1", SearchConstants.UPDATE);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.CREATE, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.RELOAD, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.CREATEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.DELETEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.DELETE, TEST_COLLECTION_NAME1);
+
+
+ /**
+ * user2->group2->role2
+ * grant QUERY privilege on collection admin and collection1 to role2
+ */
+ grantor = "user2";
+ client.grantCollectionPrivilege(ADMIN_COLLECTION_NAME, ADMIN_USER, "role2", SearchConstants.QUERY);
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role2", SearchConstants.QUERY);
+
+ verifyCollectionAdminOpFail(grantor, CollectionAction.CREATE, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.RELOAD, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.CREATEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.DELETEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.DELETE, TEST_COLLECTION_NAME1);
+
+ //grant UPDATE privilege on collection collection1 to role2, create collection1 will be failed
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role2", SearchConstants.UPDATE);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.CREATE, TEST_COLLECTION_NAME1);
+
+ //grant UPDATE privilege on collection admin to role2, create collection1 will be successful.
+ client.grantCollectionPrivilege(ADMIN_COLLECTION_NAME, ADMIN_USER, "role2", SearchConstants.UPDATE);
+
+ verifyCollectionAdminOpPass(grantor, CollectionAction.CREATE, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.RELOAD, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.CREATEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.DELETEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.DELETE, TEST_COLLECTION_NAME1);
+
+ grantor = "user3";
+
+ verifyCollectionAdminOpFail(grantor, CollectionAction.CREATE, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.RELOAD, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.CREATEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.DELETEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpFail(grantor, CollectionAction.DELETE, TEST_COLLECTION_NAME1);
+
+ /**
+ * user3->group3->role3
+ * grant UPDATE privilege on collection admin to role3
+ * grant QUERY privilege on collection collection1 to role3
+ */
+ client.grantCollectionPrivilege(ADMIN_COLLECTION_NAME, ADMIN_USER, "role3", SearchConstants.ALL);
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role3", SearchConstants.ALL);
+
+ verifyCollectionAdminOpPass(grantor, CollectionAction.CREATE, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.RELOAD, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.CREATEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.DELETEALIAS, TEST_COLLECTION_NAME1);
+ verifyCollectionAdminOpPass(grantor, CollectionAction.DELETE, TEST_COLLECTION_NAME1);
+ }
+
+ /**
+ * Test when the collection has been deleted, the privileges in the sentry service also should be deleted
+ * @throws Exception
+ */
+ @Test
+ public void testSyncPrivilegesWithDeleteCollection() throws Exception {
+ /**
+ * Upload configs to ZK for create collection
+ */
+ uploadConfigDirToZk(COLLECTION_CONFIG_DIR);
+ /**
+ * user0->group0->role0
+ * Grant ALL privilege on collection collection1 to role0
+ * Grant ALL privilege on collection admin to role0
+ * user0 can execute create & delete collection1 operation
+ */
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role0", SearchConstants.ALL);
+ client.grantCollectionPrivilege(ADMIN_COLLECTION_NAME, ADMIN_USER, "role0", SearchConstants.ALL);
+
+ assertTrue("user0 has one privilege on collection admin",
+ client.listPrivilegesByRoleName("user0", "role0", Arrays.asList(new Collection(ADMIN_COLLECTION_NAME))).size() == 1);
+
+ assertTrue("user0 has one privilege on collection collection1",
+ client.listPrivilegesByRoleName("user0", "role0", Arrays.asList(new Collection(TEST_COLLECTION_NAME1))).size() == 1);
+
+ /**
+ * user1->group1->role1
+ * grant QUERY privilege on collection collection1 to role1
+ */
+
+ client.listPrivilegesByRoleName("user0", "role0", null);
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role1", SearchConstants.ALL);
+ assertTrue("user1 has one privilege record",
+ client.listPrivilegesByRoleName("user1", "role1", Arrays.asList(new Collection(TEST_COLLECTION_NAME1))).size() == 1);
+
+ /**
+ * create collection collection1
+ */
+ setupCollection(TEST_COLLECTION_NAME1);
+ /**
+ * delete the collection1
+ */
+ deleteCollection(TEST_COLLECTION_NAME1);
+
+ //check the user0
+ assertTrue("user0 has one privilege on collection admin",
+ client.listPrivilegesByRoleName("user0", "role0", Arrays.asList(new Collection(ADMIN_COLLECTION_NAME))).size() == 1);
+
+ assertTrue("user0 has no privilege on collection collection1",
+ client.listPrivilegesByRoleName("user0", "role0", Arrays.asList(new Collection(TEST_COLLECTION_NAME1))).size() == 0);
+
+ //check the user1
+ assertTrue("user1 has no privilege on collection collection1",
+ client.listPrivilegesByRoleName("user1", "role1", Arrays.asList(new Collection(TEST_COLLECTION_NAME1))).size() == 0);
+
+
+ /**
+ * user2->group2->role2
+ * Grant UPDATE privilege on collection collection1 to role2
+ */
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role2", SearchConstants.UPDATE);
+
+ assertTrue("user2 has one privilege on collection collection1",
+ client.listPrivilegesByRoleName("user2", "role2", Arrays.asList(new Collection(TEST_COLLECTION_NAME1))).size() == 1);
+
+ /**
+ * user3->group3->role3
+ * grant QUERY privilege on collection collection1 to role3
+ */
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role3", SearchConstants.QUERY);
+ assertTrue("user1 has one privilege record",
+ client.listPrivilegesByRoleName("user3", "role3", Arrays.asList(new Collection(TEST_COLLECTION_NAME1))).size() == 1);
+
+ /**
+ * create collection collection1
+ */
+ setupCollection(TEST_COLLECTION_NAME1);
+ /**
+ * delete the collection1
+ */
+ deleteCollection(TEST_COLLECTION_NAME1);
+
+ //check the user2
+ assertTrue("user2 has no privilege on collection collection1",
+ client.listPrivilegesByRoleName("user2", "role2", Arrays.asList(new Collection(TEST_COLLECTION_NAME1))).size() == 0);
+
+ //check the user3
+ assertTrue("user3 has no privilege on collection collection1",
+ client.listPrivilegesByRoleName("user3", "role3", Arrays.asList(new Collection(TEST_COLLECTION_NAME1))).size() == 0);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrDocLevelOperations.java
----------------------------------------------------------------------
diff --git a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrDocLevelOperations.java b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrDocLevelOperations.java
new file mode 100644
index 0000000..193743b
--- /dev/null
+++ b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrDocLevelOperations.java
@@ -0,0 +1,204 @@
+/*
+ * 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.db.integration;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import org.apache.sentry.core.model.search.SearchConstants;
+import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.client.solrj.impl.CloudSolrServer;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrInputDocument;
+import org.junit.Test;
+
+public class TestSolrDocLevelOperations extends AbstractSolrSentryTestWithDbProvider {
+ private static final String TEST_COLLECTION_NAME1 = "collection1";
+ private static final String AUTH_FIELD = "sentry_auth";
+ private static final int NUM_DOCS = 100;
+
+ private void setupCollectionWithDocSecurity(String name) throws Exception {
+ String configDir = RESOURCES_DIR + File.separator + "collection1"
+ + File.separator + "conf";
+ uploadConfigDirToZk(configDir);
+ // replace solrconfig.xml with solrconfig-doc-level.xml
+ uploadConfigFileToZk(configDir + File.separator + "solrconfig-doclevel.xml",
+ "solrconfig.xml");
+ setupCollection(name);
+ }
+
+ @Test
+ public void testDocLevelOperations() throws Exception {
+ setupCollectionWithDocSecurity(TEST_COLLECTION_NAME1);
+
+ createDocument(TEST_COLLECTION_NAME1);
+
+ CloudSolrServer server = getCloudSolrServer(TEST_COLLECTION_NAME1);
+ try {
+ // queries
+ SolrQuery query = new SolrQuery();
+ query.setQuery("*:*");
+
+ // as admin
+ setAuthenticationUser(ADMIN_USER);
+ QueryResponse rsp = server.query(query);
+ SolrDocumentList docList = rsp.getResults();
+ assertEquals(NUM_DOCS, docList.getNumFound());
+
+ // as user0
+ setAuthenticationUser("user0");
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role0", SearchConstants.QUERY);
+ rsp = server.query(query);
+ docList = rsp.getResults();
+ assertEquals(NUM_DOCS/4, rsp.getResults().getNumFound());
+
+ //as user1
+ setAuthenticationUser("user1");
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role1", SearchConstants.QUERY);
+ rsp = server.query(query);
+ docList = rsp.getResults();
+ assertEquals(NUM_DOCS/4, rsp.getResults().getNumFound()); docList = rsp.getResults();
+ assertEquals(NUM_DOCS/4, rsp.getResults().getNumFound());
+
+ //as user2
+ setAuthenticationUser("user2");
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role2", SearchConstants.QUERY);
+ rsp = server.query(query);
+ docList = rsp.getResults();
+ assertEquals(NUM_DOCS/4, rsp.getResults().getNumFound());
+
+ //as user3
+ setAuthenticationUser("user3");
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role3", SearchConstants.QUERY);
+ rsp = server.query(query);
+ docList = rsp.getResults();
+ assertEquals(NUM_DOCS/4, rsp.getResults().getNumFound());
+ } finally {
+ server.shutdown();
+ }
+
+ deleteCollection(TEST_COLLECTION_NAME1);
+ }
+
+ @Test
+ public void updateDocsTest() throws Exception {
+ setupCollectionWithDocSecurity(TEST_COLLECTION_NAME1);
+
+ createDocument(TEST_COLLECTION_NAME1);
+
+ CloudSolrServer server = getCloudSolrServer(TEST_COLLECTION_NAME1);
+ try {
+ setAuthenticationUser("user0");
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role0", SearchConstants.QUERY);
+ 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());
+
+ // overwrite the document that we can't see
+ setAuthenticationUser(ADMIN_USER);
+ ArrayList<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
+ SolrInputDocument doc = new SolrInputDocument();
+ doc.addField("id", docIdStr);
+ doc.addField("description", "description" + docIdStr);
+ doc.addField(AUTH_FIELD, "role0");
+ docs.add(doc);
+ server.add(docs);
+ server.commit();
+
+ // verify we can now view the document
+ setAuthenticationUser("user0");
+ rsp = server.query(query);
+ assertEquals(1, rsp.getResults().getNumFound());
+ } finally {
+ server.shutdown();
+ }
+
+ deleteCollection(TEST_COLLECTION_NAME1);
+ }
+
+ /**
+ * Test to validate doc level security on collections without perm for Index level auth.
+ * @throws Exception
+ */
+ @Test
+ public void indexDocAuthTests() throws Exception {
+ setupCollectionWithDocSecurity(TEST_COLLECTION_NAME1);
+ try {
+ createDocument(TEST_COLLECTION_NAME1);
+ // test query for "*:*" fails as user0 (user0 doesn't have index level permissions but has doc level permissions set)
+ verifyQueryFail("user0", TEST_COLLECTION_NAME1, ALL_DOCS);
+ verifyQueryFail("user1", TEST_COLLECTION_NAME1, ALL_DOCS);
+ verifyQueryFail("user2", TEST_COLLECTION_NAME1, ALL_DOCS);
+ verifyQueryFail("user3", TEST_COLLECTION_NAME1, ALL_DOCS);
+
+ } finally {
+ deleteCollection(TEST_COLLECTION_NAME1);
+ }
+ }
+
+ /**
+ * Creates docs as follows and verifies queries work as expected:
+ * - creates NUM_DOCS documents, where the document id equals the order
+ * it was created in, starting at 0
+ * - when id % 4 == 0, documents get "role0" auth token
+ * - when id % 4 == 1, documents get "role1" auth token
+ * - when id % 4 == 2, documents get "role2" auth token
+ * - when id % 4 == 3, documents get "role3" auth token
+ * - all documents get a admin role
+ */
+ private void createDocument(String collectionName) throws Exception {
+ // 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);
+
+ if (i % 4 == 0) {
+ doc.addField(AUTH_FIELD, "role0");
+ } else if (i % 4 ==1) {
+ doc.addField(AUTH_FIELD, "role1");
+ } else if (i % 4 ==2) {
+ doc.addField(AUTH_FIELD, "role2");
+ } else {
+ doc.addField(AUTH_FIELD, "role3");
+ }
+ doc.addField(AUTH_FIELD, ADMIN_ROLE);
+ docs.add(doc);
+ }
+
+ setAuthenticationUser(ADMIN_USER);
+ CloudSolrServer server = getCloudSolrServer(collectionName);
+ try {
+ server.add(docs);
+ server.commit(true, true);
+ } finally {
+ server.shutdown();
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrQueryOperations.java
----------------------------------------------------------------------
diff --git a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrQueryOperations.java b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrQueryOperations.java
new file mode 100644
index 0000000..afe6912
--- /dev/null
+++ b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrQueryOperations.java
@@ -0,0 +1,96 @@
+/*
+ * 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.db.integration;
+
+import java.io.File;
+
+import org.apache.sentry.core.model.search.SearchConstants;
+import org.apache.solr.common.SolrInputDocument;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Sets;
+
+public class TestSolrQueryOperations extends AbstractSolrSentryTestWithDbProvider {
+ private static final Logger LOG = LoggerFactory.getLogger(TestSolrQueryOperations.class);
+ private static final String TEST_COLLECTION_NAME1 = "collection1";
+ private static final String COLLECTION_CONFIG_DIR = RESOURCES_DIR + File.separator + "collection1" + File.separator + "conf";
+
+ @Test
+ public void testQueryOperations() throws Exception {
+ /**
+ * Upload configs to ZK for create collection
+ */
+ uploadConfigDirToZk(COLLECTION_CONFIG_DIR);
+ /**
+ * create collection collection1 as admin user
+ * and clean all document in the collection1
+ */
+ setupCollection(TEST_COLLECTION_NAME1);
+ cleanSolrCollection(TEST_COLLECTION_NAME1);
+ /**
+ * add a new document into collection1 for testing
+ */
+ SolrInputDocument solrInputDoc = createSolrTestDoc();
+ uploadSolrDoc(TEST_COLLECTION_NAME1, solrInputDoc);
+
+ /**
+ * user0->group0->role0
+ * grant ALL privilege on collection collection1 to role0
+ */
+ String grantor = "user0";
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role0", SearchConstants.ALL);
+ verifyQueryPass(grantor, TEST_COLLECTION_NAME1, ALL_DOCS);
+
+ client.revokeCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role0", SearchConstants.UPDATE);
+ verifyQueryPass(grantor, TEST_COLLECTION_NAME1, ALL_DOCS);
+
+ client.revokeCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role0", SearchConstants.QUERY);
+ verifyQueryFail(grantor, TEST_COLLECTION_NAME1, ALL_DOCS);
+
+ /**
+ * user1->group1->role1
+ * grant QUERY privilege on collection collection1 to role1
+ */
+ grantor = "user1";
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role1", SearchConstants.QUERY);
+ verifyQueryPass(grantor, TEST_COLLECTION_NAME1, ALL_DOCS);
+
+ client.revokeCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role1", SearchConstants.QUERY);
+ verifyQueryFail(grantor, TEST_COLLECTION_NAME1, ALL_DOCS);
+
+ /**
+ * user2->group2->role2
+ * grant UPDATE privilege on collection collection1 to role2
+ */
+ grantor = "user2";
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role2", SearchConstants.UPDATE);
+ verifyQueryFail(grantor, TEST_COLLECTION_NAME1, ALL_DOCS);
+
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role2", SearchConstants.QUERY);
+ verifyQueryPass(grantor, TEST_COLLECTION_NAME1, ALL_DOCS);
+
+ client.renameCollectionPrivilege(TEST_COLLECTION_NAME1, "new_" + TEST_COLLECTION_NAME1, ADMIN_USER);
+ verifyQueryFail(grantor, TEST_COLLECTION_NAME1, ALL_DOCS);
+
+ grantor = "user3";
+ verifyQueryFail(grantor, TEST_COLLECTION_NAME1, ALL_DOCS);
+
+ deleteCollection(TEST_COLLECTION_NAME1);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/3893e22d/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrUpdateOperations.java
----------------------------------------------------------------------
diff --git a/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrUpdateOperations.java b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrUpdateOperations.java
new file mode 100644
index 0000000..de18979
--- /dev/null
+++ b/sentry-tests/sentry-tests-solr/src/test/java/org/apache/sentry/tests/e2e/solr/db/integration/TestSolrUpdateOperations.java
@@ -0,0 +1,105 @@
+/*
+ * 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.db.integration;
+
+import java.io.File;
+
+import org.apache.sentry.core.model.search.SearchConstants;
+import org.apache.solr.common.SolrInputDocument;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Sets;
+
+public class TestSolrUpdateOperations extends AbstractSolrSentryTestWithDbProvider {
+ private static final Logger LOG = LoggerFactory.getLogger(TestSolrUpdateOperations.class);
+ private static final String TEST_COLLECTION_NAME1 = "collection1";
+ private static final String COLLECTION_CONFIG_DIR = RESOURCES_DIR + File.separator + "collection1" + File.separator + "conf";
+
+ @Test
+ public void testUpdateOperations() throws Exception {
+ /**
+ * Upload configs to ZK for create collection
+ */
+ uploadConfigDirToZk(COLLECTION_CONFIG_DIR);
+ /**
+ * create collection collection1 as admin user
+ * and clean all document in the collection1
+ */
+ setupCollection(TEST_COLLECTION_NAME1);
+ cleanSolrCollection(TEST_COLLECTION_NAME1);
+
+ SolrInputDocument solrInputDoc = createSolrTestDoc();
+
+ /**
+ * user0->group0->role0
+ * grant ALL privilege on collection collection1 to role0
+ */
+ String grantor = "user0";
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role0", SearchConstants.ALL);
+ cleanSolrCollection(TEST_COLLECTION_NAME1);
+ verifyUpdatePass(grantor, TEST_COLLECTION_NAME1, solrInputDoc);
+ verifyDeletedocsPass(grantor, TEST_COLLECTION_NAME1, false);
+
+ //drop privilege
+ client.dropCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER);
+ verifyUpdateFail(grantor, TEST_COLLECTION_NAME1, solrInputDoc);
+ uploadSolrDoc(TEST_COLLECTION_NAME1, solrInputDoc);
+ verifyDeletedocsFail(grantor, TEST_COLLECTION_NAME1, false);
+
+ /**
+ * user1->group1->role1
+ * grant UPDATE privilege on collection collection1 to role1
+ */
+ grantor = "user1";
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role1", SearchConstants.UPDATE);
+ cleanSolrCollection(TEST_COLLECTION_NAME1);
+ verifyUpdatePass(grantor, TEST_COLLECTION_NAME1, solrInputDoc);
+ verifyDeletedocsPass(grantor, TEST_COLLECTION_NAME1, false);
+
+ //revoke privilege
+ client.revokeCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role1", SearchConstants.ALL);
+ verifyUpdateFail(grantor, TEST_COLLECTION_NAME1, solrInputDoc);
+ uploadSolrDoc(TEST_COLLECTION_NAME1, solrInputDoc);
+ verifyDeletedocsFail(grantor, TEST_COLLECTION_NAME1, false);
+
+ /**
+ * user2->group2->role2
+ * grant QUERY privilege on collection collection1 to role2
+ */
+ grantor = "user2";
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role2", SearchConstants.QUERY);
+ cleanSolrCollection(TEST_COLLECTION_NAME1);
+ verifyUpdateFail(grantor, TEST_COLLECTION_NAME1, solrInputDoc);
+ uploadSolrDoc(TEST_COLLECTION_NAME1, solrInputDoc);
+ verifyDeletedocsFail(grantor, TEST_COLLECTION_NAME1, false);
+
+ client.grantCollectionPrivilege(TEST_COLLECTION_NAME1, ADMIN_USER, "role2", SearchConstants.ALL);
+ cleanSolrCollection(TEST_COLLECTION_NAME1);
+ verifyUpdatePass(grantor, TEST_COLLECTION_NAME1, solrInputDoc);
+ verifyDeletedocsPass(grantor, TEST_COLLECTION_NAME1, false);
+
+ grantor = "user3";
+ cleanSolrCollection(TEST_COLLECTION_NAME1);
+ verifyUpdateFail(grantor, TEST_COLLECTION_NAME1, solrInputDoc);
+ uploadSolrDoc(TEST_COLLECTION_NAME1, solrInputDoc);
+ verifyDeletedocsFail(grantor, TEST_COLLECTION_NAME1, false);
+
+ deleteCollection(TEST_COLLECTION_NAME1);
+ }
+}
\ No newline at end of file