SENTRY-574: Add Sentry solr handler (Prasad Mujumdar via Gregory Chanan)
Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/60bb1003 Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/60bb1003 Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/60bb1003 Branch: refs/heads/master Commit: 60bb10037aa0f5fc197262316e7c5040987874c1 Parents: 4796ec9 Author: Gregory Chanan <[email protected]> Authored: Wed Dec 17 18:06:28 2014 -0800 Committer: Gregory Chanan <[email protected]> Committed: Wed Dec 17 18:06:28 2014 -0800 ---------------------------------------------------------------------- pom.xml | 45 +- sentry-dist/pom.xml | 4 + sentry-dist/src/main/assembly/bin.xml | 1 + sentry-dist/src/main/assembly/src.xml | 2 + sentry-solr/pom.xml | 36 ++ sentry-solr/solr-sentry-handlers/pom.xml | 118 +++++ .../SecureDocumentAnalysisRequestHandler.java | 32 ++ .../SecureFieldAnalysisRequestHandler.java | 32 ++ .../solr/handler/SecureReplicationHandler.java | 37 ++ .../solr/handler/SecureRequestHandlerUtil.java | 72 +++ .../solr/handler/admin/SecureAdminHandlers.java | 186 +++++++ .../handler/admin/SecureCollectionsHandler.java | 81 +++ .../handler/admin/SecureCoreAdminHandler.java | 155 ++++++ .../solr/handler/admin/SecureInfoHandler.java | 39 ++ .../QueryDocAuthorizationComponent.java | 118 +++++ .../QueryIndexAuthorizationComponent.java | 82 +++ .../SentryIndexAuthorizationSingleton.java | 193 ++++++++ .../UpdateIndexAuthorizationProcessor.java | 97 ++++ ...pdateIndexAuthorizationProcessorFactory.java | 42 ++ .../src/main/resources/log4j.properties | 29 ++ .../sentry/test-authz-provider.ini | 35 ++ .../solr/collection1/conf/schema-minimal.xml | 25 + .../collection1/conf/solrconfig-secureadmin.xml | 494 +++++++++++++++++++ .../solr/collection1/conf/solrconfig.xml | 436 ++++++++++++++++ .../sentry-handlers/solr/collection1/lib/README | 18 + .../lib/classes/empty-file-main-lib.txt | 1 + .../handler/TestSecureAnalysisHandlers.java | 82 +++ .../handler/TestSecureReplicationHandler.java | 64 +++ .../handler/admin/SecureAdminHandlersTest.java | 182 +++++++ .../admin/SecureCollectionsHandlerTest.java | 84 ++++ .../admin/SecureCoreAdminHandlerTest.java | 173 +++++++ .../handler/admin/SecureInfoHandlerTest.java | 105 ++++ .../QueryDocAuthorizationComponentTest.java | 266 ++++++++++ .../QueryIndexAuthorizationComponentTest.java | 128 +++++ .../SentryIndexAuthorizationSingletonTest.java | 235 +++++++++ .../sentry/SentrySingletonTestInstance.java | 94 ++++ .../org/apache/solr/sentry/SentryTestBase.java | 179 +++++++ .../UpdateIndexAuthorizationProcessorTest.java | 181 +++++++ sentry-tests/sentry-tests-solr/pom.xml | 2 +- 39 files changed, 4179 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 49bcf98..fa88493 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,7 @@ limitations under the License. <maven.enforcer.plugin.version>1.3.1</maven.enforcer.plugin.version> <build.helper.maven.plugin.version>1.8</build.helper.maven.plugin.version> <commons.lang.version>2.6</commons.lang.version> + <commons.logging.version>1.2</commons.logging.version> <bonecp.version>0.7.1.RELEASE</bonecp.version> <datanucleus.maven.plugin.version>3.3.0-release</datanucleus.maven.plugin.version> <datanucleus-api-jdo.version>3.2.6</datanucleus-api-jdo.version> @@ -75,12 +76,12 @@ limitations under the License. <libthrift.version>0.9.2</libthrift.version> <libfb303.version>0.9.2</libfb303.version> <curator.version>2.6.0</curator.version> + <junit.version>4.10</junit.version> <log4j.version>1.2.16</log4j.version> <mockito.version>1.8.5</mockito.version> <shiro.version>1.2.1</shiro.version> <slf4j.version>1.6.1</slf4j.version> <solr.version>4.10.2</solr.version> - <solr.sentry.handlers.version>4.4.0-cdh5.2.0-SNAPSHOT</solr.sentry.handlers.version> <zookeeper.version>3.4.5</zookeeper.version> <pig.version>0.12.0</pig.version> <jackson.version>1.8.8</jackson.version> @@ -89,6 +90,9 @@ limitations under the License. <curator.version>2.6.0</curator.version> <joda-time.version>2.5</joda-time.version> <test.sentry.hadoop.classpath>${maven.test.classpath}</test.sentry.hadoop.classpath> + <easymock.version>3.0</easymock.version> + <objenesis.version>1.2</objenesis.version> + <cglib.version>2.2</cglib.version> </properties> <dependencyManagement> @@ -109,6 +113,11 @@ limitations under the License. <version>${commons.lang.version}</version> </dependency> <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <version>${commons.logging.version}</version> + </dependency> + <dependency> <groupId>com.jolbox</groupId> <artifactId>bonecp</artifactId> <version>${bonecp.version}</version> @@ -219,19 +228,28 @@ limitations under the License. </exclusions> </dependency> <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>solr-sentry-handlers</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-test-framework</artifactId> <version>${solr.version}</version> </dependency> <dependency> <groupId>org.apache.solr</groupId> - <artifactId>solr-sentry-handlers</artifactId> - <version>${solr.sentry.handlers.version}</version> - <scope>test</scope> + <artifactId>solr-solrj</artifactId> + <version>${solr.version}</version> </dependency> <dependency> <groupId>org.apache.solr</groupId> - <artifactId>solr-solrj</artifactId> + <artifactId>solr-core</artifactId> + <version>${solr.version}</version> + </dependency> + <dependency> + <groupId>org.apache.solr</groupId> + <artifactId>solr-test-framework</artifactId> <version>${solr.version}</version> </dependency> <dependency> @@ -460,6 +478,21 @@ limitations under the License. <artifactId>joda-time</artifactId> <version>${joda-time.version}</version> </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <version>${easymock.version}</version> + </dependency> + <dependency> + <groupId>org.objenesis</groupId> + <artifactId>objenesis</artifactId> + <version>${objenesis.version}</version> + </dependency> + <dependency> + <groupId>cglib</groupId> + <artifactId>cglib-nodep</artifactId> + <version>${cglib.version}</version> + </dependency> </dependencies> </dependencyManagement> @@ -470,6 +503,7 @@ limitations under the License. <module>sentry-policy</module> <module>sentry-tests</module> <module>sentry-hdfs</module> + <module>sentry-solr</module> <module>sentry-dist</module> </modules> @@ -642,6 +676,7 @@ limitations under the License. <exclude>**.patch</exclude> <!-- Exclude generated solr config files --> <exclude>**/solr/collection1/conf/**</exclude> + <exclude>**/empty-file-main-lib.txt</exclude> <!-- Exclude generated thrift files --> <exclude>**/gen/**</exclude> <!-- README and test data with exact format --> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-dist/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-dist/pom.xml b/sentry-dist/pom.xml index 4eb1d9c..04a63de 100644 --- a/sentry-dist/pom.xml +++ b/sentry-dist/pom.xml @@ -48,6 +48,10 @@ limitations under the License. </dependency> <dependency> <groupId>org.apache.sentry</groupId> + <artifactId>solr-sentry-handlers</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> <artifactId>sentry-provider-common</artifactId> </dependency> <dependency> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-dist/src/main/assembly/bin.xml ---------------------------------------------------------------------- diff --git a/sentry-dist/src/main/assembly/bin.xml b/sentry-dist/src/main/assembly/bin.xml index f1d301b..beaa348 100644 --- a/sentry-dist/src/main/assembly/bin.xml +++ b/sentry-dist/src/main/assembly/bin.xml @@ -92,6 +92,7 @@ <exclude>sentry-policy/**</exclude> <exclude>sentry-tests/**</exclude> <exclude>sentry-hdfs/**</exclude> + <exclude>sentry-solr/**</exclude> </excludes> <includes> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-dist/src/main/assembly/src.xml ---------------------------------------------------------------------- diff --git a/sentry-dist/src/main/assembly/src.xml b/sentry-dist/src/main/assembly/src.xml index a06e521..c730c58 100644 --- a/sentry-dist/src/main/assembly/src.xml +++ b/sentry-dist/src/main/assembly/src.xml @@ -59,6 +59,8 @@ <include>sentry-provider/**</include> <include>sentry-policy/**</include> <include>sentry-tests/**</include> + <include>sentry-hdfs/**</include> + <include>sentry-solr/**</include> </includes> <outputDirectory>/</outputDirectory> </fileSet> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-solr/pom.xml b/sentry-solr/pom.xml new file mode 100644 index 0000000..c243802 --- /dev/null +++ b/sentry-solr/pom.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry</artifactId> + <version>1.5.0-incubating-SNAPSHOT</version> + </parent> + + <artifactId>sentry-solr</artifactId> + <name>Sentry Solr</name> + <packaging>pom</packaging> + + <modules> + <module>solr-sentry-handlers</module> + </modules> + +</project> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/pom.xml b/sentry-solr/solr-sentry-handlers/pom.xml new file mode 100644 index 0000000..8ca1cb3 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/pom.xml @@ -0,0 +1,118 @@ +<?xml version="1.0"?> +<!-- +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. +--> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-solr</artifactId> + <version>1.5.0-incubating-SNAPSHOT</version> + </parent> + + <artifactId>solr-sentry-handlers</artifactId> + <name>Solr Sentry handler</name> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-cli</groupId> + <artifactId>commons-cli</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-core-common</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-core-model-search</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-provider-common</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-provider-file</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-policy-common</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-policy-search</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-binding-solr</artifactId> + </dependency> + <dependency> + <groupId>org.apache.solr</groupId> + <artifactId>solr-test-framework</artifactId> + </dependency> + <dependency> + <groupId>org.apache.solr</groupId> + <artifactId>solr-solrj</artifactId> + </dependency> + <dependency> + <groupId>org.apache.solr</groupId> + <artifactId>solr-core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.shiro</groupId> + <artifactId>shiro-core</artifactId> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.objenesis</groupId> + <artifactId>objenesis</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>cglib</groupId> + <artifactId>cglib-nodep</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + +</project> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureDocumentAnalysisRequestHandler.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureDocumentAnalysisRequestHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureDocumentAnalysisRequestHandler.java new file mode 100644 index 0000000..23886fe --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureDocumentAnalysisRequestHandler.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.handler; + +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; + +/** + * Secure (sentry-aware) version of DocumentAnalysisRequestHandler + */ +public class SecureDocumentAnalysisRequestHandler extends DocumentAnalysisRequestHandler { + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + SecureRequestHandlerUtil.checkSentryCollection(req, SecureRequestHandlerUtil.QUERY_ONLY); + super.handleRequestBody(req, rsp); + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureFieldAnalysisRequestHandler.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureFieldAnalysisRequestHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureFieldAnalysisRequestHandler.java new file mode 100644 index 0000000..4a8809a --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureFieldAnalysisRequestHandler.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.solr.handler; + +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; + +/** + * Secure (sentry-aware) version of FieldAnalysisRequestHandler + */ +public class SecureFieldAnalysisRequestHandler extends FieldAnalysisRequestHandler { + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + SecureRequestHandlerUtil.checkSentryCollection(req, SecureRequestHandlerUtil.QUERY_ONLY); + super.handleRequestBody(req, rsp); + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureReplicationHandler.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureReplicationHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureReplicationHandler.java new file mode 100644 index 0000000..70e5c83 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureReplicationHandler.java @@ -0,0 +1,37 @@ +/* + * 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.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; + +/** + * Secure (sentry-aware) version of ReplicationHandler + */ +public class SecureReplicationHandler extends ReplicationHandler { + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + String collection = null; + if (req.getCore() == null) { + // if the request doesn't have a core, let's use the core stored on the + // request handler + collection = core.getCoreDescriptor().getCloudDescriptor().getCollectionName(); + } + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_AND_UPDATE, true, collection); + super.handleRequestBody(req, rsp); + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/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 new file mode 100644 index 0000000..8b46388 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRequestHandlerUtil.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.solr.handler; + +import java.util.EnumSet; +import java.util.Set; +import org.apache.sentry.core.model.search.SearchModelAction; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.sentry.SentryIndexAuthorizationSingleton; + +/** + * Utility functions for Secure (sentry-aware) versions of RequestHandlers + */ +public class SecureRequestHandlerUtil { + public static final Set<SearchModelAction> QUERY_ONLY = EnumSet.of(SearchModelAction.QUERY); + public static final Set<SearchModelAction> UPDATE_ONLY = EnumSet.of(SearchModelAction.UPDATE); + public static final Set<SearchModelAction> QUERY_AND_UPDATE = EnumSet.of(SearchModelAction.QUERY, SearchModelAction.UPDATE); + + // Hack to provide a test-only version of SentryIndexAuthorizationSingleton + public static SentryIndexAuthorizationSingleton testOverride = null; + + /** + * Attempt to authorize an administrative action. + * + * @param req request to check + * @param andActions set of actions to check + * @param checkCollection check the collection the action is on, or only "admin"? + * @param collection only relevant if checkCollection==true, + * use collection (if non-null) instead pulling collection name from req (if null) + */ + public static void checkSentryAdmin(SolrQueryRequest req, Set<SearchModelAction> andActions, boolean checkCollection, String collection) { + checkSentry(req, andActions, true, checkCollection, collection); + } + + /** + * Attempt to authorize a collection action. The collection + * name will be pulled from the request. + */ + public static void checkSentryCollection(SolrQueryRequest req, Set<SearchModelAction> andActions) { + checkSentry(req, andActions, false, false, null); + } + + 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 + // actions one at a time + final SentryIndexAuthorizationSingleton sentryInstance = + (testOverride == null)?SentryIndexAuthorizationSingleton.getInstance():testOverride; + for (SearchModelAction action : andActions) { + if (admin) { + sentryInstance.authorizeAdminAction(req, EnumSet.of(action), checkCollection, collection); + } else { + sentryInstance.authorizeCollectionAction(req, EnumSet.of(action)); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureAdminHandlers.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureAdminHandlers.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureAdminHandlers.java new file mode 100644 index 0000000..5463754 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureAdminHandlers.java @@ -0,0 +1,186 @@ +/* + * 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.admin; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.Map; + +import org.apache.solr.common.SolrException; +import org.apache.solr.core.CoreContainer; +import org.apache.solr.core.SolrCore; +import org.apache.sentry.core.model.search.SearchModelAction; +import org.apache.solr.handler.RequestHandlerBase; +import org.apache.solr.handler.SecureRequestHandlerUtil; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.request.SolrRequestHandler; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.util.plugin.SolrCoreAware; +import org.apache.zookeeper.KeeperException; + +/** + * Secure version of AdminHandlers that creates Sentry-aware AdminHandlers. + */ +public class SecureAdminHandlers extends AdminHandlers { + + protected static class StandardHandler { + final String name; + final SolrRequestHandler handler; + + public StandardHandler( String n, SolrRequestHandler h ) + { + this.name = n; + this.handler = h; + } + } + + /** + * NOTE: ideally we'd just override the list of admin handlers, but + * since AdminHandlers in solr doesn't allow it, let's just copy the + * entire function. This is deprecated in Solr 5.0, so we should do something + * different with Solr 5.0 anyway. + */ + @Override + public void inform(SolrCore core) + { + String path = null; + for( Map.Entry<String, SolrRequestHandler> entry : core.getRequestHandlers().entrySet() ) { + if( entry.getValue() == this ) { + path = entry.getKey(); + break; + } + } + if( path == null ) { + throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, + "The AdminHandler is not registered with the current core." ); + } + if( !path.startsWith( "/" ) ) { + throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, + "The AdminHandler needs to be registered to a path. Typically this is '/admin'" ); + } + // Remove the parent handler + core.registerRequestHandler(path, null); + if( !path.endsWith( "/" ) ) { + path += "/"; + } + + StandardHandler[] list = new StandardHandler[] { + new StandardHandler( "luke", new SecureLukeRequestHandler() ), + new StandardHandler( "system", new SecureSystemInfoHandler() ), + new StandardHandler( "mbeans", new SecureSolrInfoMBeanHandler() ), + new StandardHandler( "plugins", new SecurePluginInfoHandler() ), + new StandardHandler( "threads", new SecureThreadDumpHandler() ), + new StandardHandler( "properties", new SecurePropertiesRequestHandler() ), + new StandardHandler( "logging", new SecureLoggingHandler() ), + new StandardHandler( "file", new SecureShowFileRequestHandler() ) + }; + + for( StandardHandler handler : list ) { + if( core.getRequestHandler( path+handler.name ) == null ) { + handler.handler.init( initArgs ); + core.registerRequestHandler( path+handler.name, handler.handler ); + if( handler.handler instanceof SolrCoreAware ) { + ((SolrCoreAware)handler.handler).inform(core); + } + } + } + } + + public static class SecureLoggingHandler extends LoggingHandler { + public SecureLoggingHandler(CoreContainer cc) { + super(cc); + } + + public SecureLoggingHandler() { + super(); + } + + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + // logging handler can be used both to read and change logs + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_AND_UPDATE, false, null); + super.handleRequestBody(req, rsp); + } + } + + public static class SecureLukeRequestHandler extends LukeRequestHandler { + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_ONLY, true, null); + super.handleRequestBody(req, rsp); + } + } + + public static class SecurePluginInfoHandler extends PluginInfoHandler { + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_ONLY, true, null); + super.handleRequestBody(req, rsp); + } + } + + public static class SecurePropertiesRequestHandler extends PropertiesRequestHandler { + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_ONLY, false, null); + super.handleRequestBody(req, rsp); + } + } + + public static class SecureShowFileRequestHandler extends ShowFileRequestHandler { + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) + throws IOException, KeeperException, InterruptedException { + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_ONLY, true, null); + super.handleRequestBody(req, rsp); + } + } + + public static class SecureSolrInfoMBeanHandler extends SolrInfoMBeanHandler { + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_ONLY, true, null); + super.handleRequestBody(req, rsp); + } + } + + public static class SecureSystemInfoHandler extends SystemInfoHandler { + public SecureSystemInfoHandler() { + super(); + } + + public SecureSystemInfoHandler(CoreContainer cc) { + super(cc); + } + + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + // this may or may not have the core + SolrCore core = req.getCore(); + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_ONLY, core != null, null); + super.handleRequestBody(req, rsp); + } + } + + public static class SecureThreadDumpHandler extends ThreadDumpHandler { + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_ONLY, false, null); + super.handleRequestBody(req, rsp); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/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 new file mode 100644 index 0000000..20d9626 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java @@ -0,0 +1,81 @@ +package org.apache.solr.handler.admin; + +/* + * 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. + */ + +import java.util.EnumSet; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.params.CollectionParams.CollectionAction; +import org.apache.solr.common.params.CoreAdminParams; +import org.apache.sentry.core.model.search.SearchModelAction; +import org.apache.solr.handler.SecureRequestHandlerUtil; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.core.CoreContainer; + +/** + * Secure (sentry-aware) version of CollectionsHandler + */ +public class SecureCollectionsHandler extends CollectionsHandler { + + public SecureCollectionsHandler() { + super(); + } + + public SecureCollectionsHandler(final CoreContainer coreContainer) { + super(coreContainer); + } + + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + // Pick the action + SolrParams params = req.getParams(); + CollectionAction action = null; + String a = params.get(CoreAdminParams.ACTION); + String collection = null; + if (a != null) { + action = CollectionAction.get(a); + } + if (action != null) { + switch (action) { + case CREATE: + case DELETE: + case RELOAD: + case CREATEALIAS: // FixMe: do we need to check the underlying "collections" as well? + case DELETEALIAS: + { + collection = req.getParams().required().get("name"); + break; + } + case SYNCSHARD: + case SPLITSHARD: + case DELETESHARD: { + collection = req.getParams().required().get("collection"); + break; + } + default: { + collection = null; + break; + } + } + } + // all actions require UPDATE privileges + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.UPDATE_ONLY, + true, collection); + super.handleRequestBody(req, rsp); + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCoreAdminHandler.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCoreAdminHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCoreAdminHandler.java new file mode 100644 index 0000000..c1bde31 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCoreAdminHandler.java @@ -0,0 +1,155 @@ +package org.apache.solr.handler.admin; + +/* + * 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. + */ + +import java.util.EnumSet; +import org.apache.solr.core.SolrCore; +import org.apache.sentry.core.model.search.SearchModelAction; +import org.apache.solr.common.params.CoreAdminParams; +import org.apache.solr.common.params.CoreAdminParams.CoreAdminAction; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.handler.SecureRequestHandlerUtil; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.core.CoreContainer; + +/** + * Secure (sentry-aware) version of CoreAdminHandler + */ +public class SecureCoreAdminHandler extends CoreAdminHandler { + + public SecureCoreAdminHandler() { + super(); + } + + public SecureCoreAdminHandler(final CoreContainer coreContainer) { + super(coreContainer); + } + + private String getCollectionFromCoreName(String coreName) { + SolrCore solrCore = null; + try { + if (coreName != null && !coreName.equals("")) { + solrCore = coreContainer.getCore(coreName); + if (solrCore != null) { + return solrCore.getCoreDescriptor().getCloudDescriptor().getCollectionName(); + } + } + } finally { + if (solrCore != null) { + solrCore.close(); + } + } + return null; + } + + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + SolrParams params = req.getParams(); + CoreAdminAction action = CoreAdminAction.STATUS; + String a = params.get(CoreAdminParams.ACTION); + if (a != null) { + action = CoreAdminAction.get(a); + if (action == null) { + // some custom action -- let's reqiure QUERY and UPDATE + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_AND_UPDATE, true, null); + } + } + String collection = null; + boolean checkCollection = true; + if (action != null) { + switch (action) { + case RENAME: + case UNLOAD: + case RELOAD: + case SWAP: + case MERGEINDEXES: + case SPLIT: + case PREPRECOVERY: + case REQUESTRECOVERY: + case REQUESTSYNCSHARD: { + String cname = params.get(CoreAdminParams.CORE,""); + collection = getCollectionFromCoreName(cname); + break; + } + case REQUESTAPPLYUPDATES: { + String cname = params.get(CoreAdminParams.NAME, ""); + collection = getCollectionFromCoreName(cname); + break; + } + case CREATE: { + collection = params.get(CoreAdminParams.COLLECTION); + break; + } + case STATUS: + // CORE is an optional param for STATUS, but since the + // non-parameterized version returns all the core info, it doesn't + // make sense to check for core permissions. + case PERSIST: + case CREATEALIAS: + case DELETEALIAS: + case LOAD_ON_STARTUP: + case TRANSIENT: + default: { + // these are actions that are not core related or not actually + // handled by the CoreAdminHandler + checkCollection = false; + break; + } + } + + switch (action) { + case STATUS: { + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_ONLY, checkCollection, collection); + break; + } + case LOAD: + case UNLOAD: + case RELOAD: + case CREATE: + case PERSIST: + case SWAP: + case RENAME: + case MERGEINDEXES: + case SPLIT: + case PREPRECOVERY: + case REQUESTRECOVERY: + case REQUESTSYNCSHARD: + case REQUESTAPPLYUPDATES: + // these next few aren't handled by the CoreAdminHandler currently, + // but let's check them just in case something changes + case CREATEALIAS: + case DELETEALIAS: + case LOAD_ON_STARTUP: + case REQUESTBUFFERUPDATES: + case OVERSEEROP: + case REQUESTSTATUS: + case TRANSIENT: { + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.UPDATE_ONLY, checkCollection, collection); + break; + } + default: { + // some custom action -- let's reqiure QUERY and UPDATE + SecureRequestHandlerUtil.checkSentryAdmin(req, SecureRequestHandlerUtil.QUERY_AND_UPDATE, checkCollection, collection); + break; + } + } + } + super.handleRequestBody(req, rsp); + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureInfoHandler.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureInfoHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureInfoHandler.java new file mode 100644 index 0000000..90b898b --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureInfoHandler.java @@ -0,0 +1,39 @@ +package org.apache.solr.handler.admin; + +/* + * 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. + */ + +import java.util.EnumSet; +import org.apache.sentry.core.model.search.SearchModelAction; +import org.apache.solr.handler.RequestHandlerBase; +import org.apache.solr.core.CoreContainer; + +/** + * Secure version of InfoHandler that uses Sentry-aware request handlers + */ +public class SecureInfoHandler extends InfoHandler { + + public SecureInfoHandler(final CoreContainer coreContainer) { + super(coreContainer); + + setPropertiesHandler( new SecureAdminHandlers.SecurePropertiesRequestHandler() ); + setThreadDumpHandler( new SecureAdminHandlers.SecureThreadDumpHandler() ); + setLoggingHandler( new SecureAdminHandlers.SecureLoggingHandler(coreContainer) ); + setSystemInfoHandler( new SecureAdminHandlers.SecureSystemInfoHandler(coreContainer) ); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/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 new file mode 100644 index 0000000..371787d --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/QueryDocAuthorizationComponent.java @@ -0,0 +1,118 @@ +/* + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.sentry.SentryIndexAuthorizationSingleton; +import org.apache.solr.request.LocalSolrQueryRequest; +import com.google.common.annotations.VisibleForTesting; +import java.io.IOException; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Set; +import java.net.URLEncoder; + +public class QueryDocAuthorizationComponent extends SearchComponent +{ + private static Logger log = + LoggerFactory.getLogger(QueryDocAuthorizationComponent.class); + public static String AUTH_FIELD_PROP = "sentryAuthField"; + public static String DEFAULT_AUTH_FIELD = "sentry_auth"; + public static String ALL_ROLES_TOKEN_PROP = "allRolesToken"; + public static String ENABLED_PROP = "enabled"; + private SentryIndexAuthorizationSingleton sentryInstance; + private String authField; + private String allRolesToken; + private boolean enabled; + + public QueryDocAuthorizationComponent() { + this(SentryIndexAuthorizationSingleton.getInstance()); + } + + @VisibleForTesting + public QueryDocAuthorizationComponent(SentryIndexAuthorizationSingleton sentryInstance) { + super(); + this.sentryInstance = sentryInstance; + } + + @Override + public void init(NamedList args) { + SolrParams params = SolrParams.toSolrParams(args); + this.authField = params.get(AUTH_FIELD_PROP, DEFAULT_AUTH_FIELD); + log.info("QueryDocAuthorizationComponent authField: " + this.authField); + this.allRolesToken = params.get(ALL_ROLES_TOKEN_PROP, ""); + log.info("QueryDocAuthorizationComponent allRolesToken: " + this.allRolesToken); + this.enabled = params.getBool(ENABLED_PROP, false); + log.info("QueryDocAuthorizationComponent enabled: " + this.enabled); + } + + private void addRawClause(StringBuilder builder, String authField, String value) { + // requires a space before the first term, so the + // default lucene query parser will be used + builder.append(" {!raw f=").append(authField).append(" v=") + .append(value).append("}"); + } + + @Override + public void prepare(ResponseBuilder rb) throws IOException { + if (!enabled) return; + + String userName = sentryInstance.getUserName(rb.req); + String superUser = (System.getProperty("solr.authorization.superuser", "solr")); + if (superUser.equals(userName)) { + return; + } + 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); + } + ModifiableSolrParams newParams = new ModifiableSolrParams(rb.req.getParams()); + String result = builder.toString(); + newParams.add("fq", result); + rb.req.setParams(newParams); + } else { + throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, + "Request from user: " + userName + + " rejected because user is not associated with any roles"); + } + } + + @Override + public void process(ResponseBuilder rb) throws IOException { + } + + @Override + public String getDescription() { + return "Handle Query Document Authorization"; + } + + @Override + public String getSource() { + return "$URL$"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/QueryIndexAuthorizationComponent.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/QueryIndexAuthorizationComponent.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/QueryIndexAuthorizationComponent.java new file mode 100644 index 0000000..e4b5741 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/component/QueryIndexAuthorizationComponent.java @@ -0,0 +1,82 @@ +/* + * 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.solr.common.util.StrUtils; +import org.apache.solr.sentry.SentryIndexAuthorizationSingleton; +import org.apache.sentry.core.model.search.SearchModelAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.google.common.annotations.VisibleForTesting; +import java.io.IOException; +import java.util.EnumSet; +import java.util.List; + +public class QueryIndexAuthorizationComponent extends SearchComponent +{ + private static Logger log = + LoggerFactory.getLogger(QueryIndexAuthorizationComponent.class); + private SentryIndexAuthorizationSingleton sentryInstance; + + public QueryIndexAuthorizationComponent() { + this(SentryIndexAuthorizationSingleton.getInstance()); + } + + @VisibleForTesting + public QueryIndexAuthorizationComponent(SentryIndexAuthorizationSingleton sentryInstance) { + super(); + this.sentryInstance = sentryInstance; + } + + @Override + public void prepare(ResponseBuilder rb) throws IOException { + sentryInstance.authorizeCollectionAction( + rb.req, EnumSet.of(SearchModelAction.QUERY)); + String collections = rb.req.getParams().get("collection"); + if (collections != null) { + List<String> collectionList = StrUtils.splitSmart(collections, ",", true); + // this may be an alias request: check each collection listed in "collections". + // NOTE1: this may involve checking a collection twice, because the collection + // on which this component is running may also be in the collections list, + // but this simplifies the logic. We don't want to only check the collections in the + // list, because the user can spoof this by adding collections to non-alias requests. + + // NOTE2: we only need to do this for queries, not for updates, because updates are only + // written to the first alias in the collection list, so are guaranteed to undergo the + // correct sentry check + for (String coll : collectionList) { + sentryInstance.authorizeCollectionAction(rb.req, EnumSet.of(SearchModelAction.QUERY), + coll, true); + } + } + } + + @Override + public void process(ResponseBuilder rb) throws IOException { + } + + @Override + public String getDescription() { + return "Handle Query Index Authorization"; + } + + @Override + public String getSource() { + return "$URL$"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/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 new file mode 100644 index 0000000..d44b228 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java @@ -0,0 +1,193 @@ +/* + * 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.sentry; + +import java.net.URL; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.sentry.binding.solr.authz.SentrySolrAuthorizationException; +import org.apache.sentry.binding.solr.authz.SolrAuthzBinding; +import org.apache.sentry.binding.solr.conf.SolrAuthzConf; +import org.apache.sentry.core.common.Subject; +import org.apache.sentry.core.model.search.Collection; +import org.apache.sentry.core.model.search.SearchModelAction; +import org.apache.solr.common.SolrException; +import org.apache.solr.core.SolrCore; +import org.apache.solr.request.LocalSolrQueryRequest; +import org.apache.solr.request.SolrQueryRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SentryIndexAuthorizationSingleton { + + private static Logger log = + LoggerFactory.getLogger(SentryIndexAuthorizationSingleton.class); + + public static final String propertyName = "solr.authorization.sentry.site"; + private static final String USER_NAME = "solr.user.name"; + + private static final SentryIndexAuthorizationSingleton INSTANCE = + new SentryIndexAuthorizationSingleton(System.getProperty(propertyName)); + + private final SolrAuthzBinding binding; + + private SentryIndexAuthorizationSingleton(String sentrySiteLocation) { + SolrAuthzBinding tmpBinding = null; + try { + if (sentrySiteLocation != null && sentrySiteLocation.length() > 0) { + tmpBinding = + new SolrAuthzBinding(new SolrAuthzConf(new URL("file://" + sentrySiteLocation))); + log.info("SolrAuthzBinding created successfully"); + } else { + log.info("SolrAuthzBinding not created because " + propertyName + + " not set, sentry not enabled"); + } + } catch (Exception ex) { + log.error("Unable to create SolrAuthzBinding", ex); + } + binding = tmpBinding; + } + + public static SentryIndexAuthorizationSingleton getInstance() { + return INSTANCE; + } + + /** + * Returns true iff Sentry index authorization checking is enabled + */ + public boolean isEnabled() { + return binding != null; + } + + /** + * Attempt to authorize an administrative action. + * + * @param req request to check + * @param actions set of actions to check + * @param checkCollection check the collection the action is on, or only "admin"? + * @param collection only relevant if checkCollection==true, + * use collection (if non-null) instead pulling collection name from req (if null) + */ + public void authorizeAdminAction(SolrQueryRequest req, + Set<SearchModelAction> actions, boolean checkCollection, String collection) + throws SolrException { + authorizeCollectionAction(req, actions, "admin", true); + if (checkCollection) { + // Let's not error out if we can't find the collection associated with an + // admin action, it's pretty complicated to get all the possible administrative + // actions correct. Instead, let's warn in the log and address any issues we + // find. + authorizeCollectionAction(req, actions, collection, false); + } + } + + /** + * Attempt to authorize a collection action. The collection + * name will be pulled from the request. + */ + public void authorizeCollectionAction(SolrQueryRequest req, + Set<SearchModelAction> actions) throws SolrException { + authorizeCollectionAction(req, actions, null, true); + } + + /** + * Attempt to authorize a collection action. + * + * @param req request to check + * @param actions set of actions to check + * @param collectionName the collection to check. If null, the collection + * name is pulled from the request + * @param errorIfNoCollection is true, throw an exception if collection + * cannot be located + */ + public void authorizeCollectionAction(SolrQueryRequest req, + Set<SearchModelAction> actions, String collectionName, boolean errorIfNoCollection) + throws SolrException { + + Subject superUser = new Subject(System.getProperty("solr.authorization.superuser", "solr")); + Subject userName = new Subject(getUserName(req)); + if (collectionName == null) { + SolrCore solrCore = req.getCore(); + if (solrCore == null) { + String msg = "Unable to locate collection for sentry to authorize because " + + "no SolrCore attached to request"; + if (errorIfNoCollection) { + throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, msg); + } else { // just warn + log.warn(msg); + return; + } + } + collectionName = solrCore.getCoreDescriptor().getCloudDescriptor().getCollectionName(); + } + Collection collection = new Collection(collectionName); + try { + if (!superUser.getName().equals(userName.getName())) { + binding.authorizeCollection(userName, collection, actions); + } + } catch (SentrySolrAuthorizationException ex) { + throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, ex); + } + + } + + /** + * Get the roles associated with the user + * @param userName to get roles for + * @return The roles associated with the user + */ + public Set<String> getRoles(String userName) { + if (binding == null) { + return null; + } + return binding.getRoles(userName); + } + + /** + * Get the user name associated with the request + * + * @param req the request + * @return the user name associated with the request + */ + public String getUserName(SolrQueryRequest req) throws SolrException { + if (binding == null) { + throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, + "Solr binding was not created successfully. Defaulting to no access"); + } + SolrCore solrCore = req.getCore(); + HttpServletRequest httpServletRequest = (HttpServletRequest)req.getContext().get("httpRequest"); + + // LocalSolrQueryRequests won't have the HttpServletRequest because there is no + // http request associated with it. + if (httpServletRequest == null && !(req instanceof LocalSolrQueryRequest)) { + StringBuilder builder = new StringBuilder("Unable to locate HttpServletRequest"); + if (solrCore != null && solrCore.getSolrConfig().getBool( + "requestDispatcher/requestParsers/@addHttpRequestToContext", true) == false) { + builder.append(", ensure requestDispatcher/requestParsers/@addHttpRequestToContext is set to true"); + } + throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, builder.toString()); + } + + String superUser = (System.getProperty("solr.authorization.superuser", "solr")); + // If a local request, treat it like a super user request; i.e. it is equivalent to an + // http request from the same process. + return req instanceof LocalSolrQueryRequest? + superUser:(String)httpServletRequest.getAttribute(USER_NAME); + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/update/processor/UpdateIndexAuthorizationProcessor.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/update/processor/UpdateIndexAuthorizationProcessor.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/update/processor/UpdateIndexAuthorizationProcessor.java new file mode 100644 index 0000000..8cd53d3 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/update/processor/UpdateIndexAuthorizationProcessor.java @@ -0,0 +1,97 @@ +package org.apache.solr.update.processor; + +/* + * 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. + */ + +import org.apache.solr.common.SolrException; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.sentry.SentryIndexAuthorizationSingleton; +import org.apache.solr.update.AddUpdateCommand; +import org.apache.solr.update.CommitUpdateCommand; +import org.apache.solr.update.DeleteUpdateCommand; +import org.apache.solr.update.MergeIndexesCommand; +import org.apache.solr.update.RollbackUpdateCommand; +import org.apache.sentry.core.model.search.SearchModelAction; +import com.google.common.annotations.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.EnumSet; + +public class UpdateIndexAuthorizationProcessor extends UpdateRequestProcessor { + + private SolrQueryRequest req; + private SentryIndexAuthorizationSingleton sentryInstance; + + public UpdateIndexAuthorizationProcessor(SolrQueryRequest req, + SolrQueryResponse rsp, UpdateRequestProcessor next) { + this(SentryIndexAuthorizationSingleton.getInstance(), req, rsp, next); + } + + @VisibleForTesting + public UpdateIndexAuthorizationProcessor(SentryIndexAuthorizationSingleton sentryInstance, + SolrQueryRequest req, SolrQueryResponse rsp, UpdateRequestProcessor next) { + super(next); + this.sentryInstance = sentryInstance; + this.req = req; + } + + public void authorizeCollectionAction() throws SolrException { + sentryInstance.authorizeCollectionAction( + req, EnumSet.of(SearchModelAction.UPDATE)); + } + + @Override + public void processAdd(AddUpdateCommand cmd) throws IOException { + authorizeCollectionAction(); + super.processAdd(cmd); + } + + @Override + public void processDelete(DeleteUpdateCommand cmd) throws IOException { + authorizeCollectionAction(); + super.processDelete(cmd); + } + + @Override + public void processMergeIndexes(MergeIndexesCommand cmd) throws IOException { + authorizeCollectionAction(); + super.processMergeIndexes(cmd); + } + + @Override + public void processCommit(CommitUpdateCommand cmd) throws IOException + { + authorizeCollectionAction(); + super.processCommit(cmd); + } + + @Override + public void processRollback(RollbackUpdateCommand cmd) throws IOException + { + authorizeCollectionAction(); + super.processRollback(cmd); + } + + @Override + public void finish() throws IOException { + authorizeCollectionAction(); + super.finish(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/update/processor/UpdateIndexAuthorizationProcessorFactory.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/update/processor/UpdateIndexAuthorizationProcessorFactory.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/update/processor/UpdateIndexAuthorizationProcessorFactory.java new file mode 100644 index 0000000..945dbc4 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/update/processor/UpdateIndexAuthorizationProcessorFactory.java @@ -0,0 +1,42 @@ +package org.apache.solr.update.processor; + +/* + * 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. + */ + +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.update.processor.UpdateRequestProcessorFactory; + +/** + * Factory for Sentry's index-level update authorization. + * Required to implement RunAlways so users can't bypass sentry checks. + */ +public class UpdateIndexAuthorizationProcessorFactory + extends UpdateRequestProcessorFactory + implements UpdateRequestProcessorFactory.RunAlways { + + @Override + public void init(NamedList args) { + } + + @Override + public UpdateIndexAuthorizationProcessor getInstance(SolrQueryRequest req, + SolrQueryResponse rsp, UpdateRequestProcessor next) { + return new UpdateIndexAuthorizationProcessor(req, rsp, next); + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/resources/log4j.properties b/sentry-solr/solr-sentry-handlers/src/main/resources/log4j.properties new file mode 100644 index 0000000..62fdcd4 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/resources/log4j.properties @@ -0,0 +1,29 @@ +# +# 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. +# + +# Logging level +log4j.rootLogger=INFO, CONSOLE + +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.Target=System.err +log4j.appender.CONSOLE.layout=org.apache.solr.util.SolrLogLayout +log4j.appender.CONSOLE.layout.ConversionPattern=%-5p - %d{yyyy-MM-dd HH:mm:ss.SSS}; %C; %m\n + +log4j.logger.org.apache.zookeeper=WARN +log4j.logger.org.apache.hadoop=WARN http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/resources/sentry-handlers/sentry/test-authz-provider.ini ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/resources/sentry-handlers/sentry/test-authz-provider.ini b/sentry-solr/solr-sentry-handlers/src/main/resources/sentry-handlers/sentry/test-authz-provider.ini new file mode 100644 index 0000000..8f48a8c --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/resources/sentry-handlers/sentry/test-authz-provider.ini @@ -0,0 +1,35 @@ +# 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. + +[groups] +junit = junit_role +queryOnlyAdmin = queryOnlyAdmin_role +updateOnlyAdmin = updateOnlyAdmin_role +undefinedRoleGroup = undefinedRole + +[roles] +junit_role = collection=admin, collection=collection1, collection=queryCollection->action=query, \ + collection=updateCollection->action=update +queryOnlyAdmin_role = collection=admin->action=query +updateOnlyAdmin_role = collection=admin->action=update + +[users] +junit=junit +queryOnlyAdmin=queryOnlyAdmin +updateOnlyAdmin=updateOnlyAdmin +multiGroupUser=junit, queryOnlyAdmin, updateOnlyAdmin +undefinedRoleUser=undefinedRoleGroup http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/60bb1003/sentry-solr/solr-sentry-handlers/src/main/resources/sentry-handlers/solr/collection1/conf/schema-minimal.xml ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/resources/sentry-handlers/solr/collection1/conf/schema-minimal.xml b/sentry-solr/solr-sentry-handlers/src/main/resources/sentry-handlers/solr/collection1/conf/schema-minimal.xml new file mode 100644 index 0000000..9e2f947 --- /dev/null +++ b/sentry-solr/solr-sentry-handlers/src/main/resources/sentry-handlers/solr/collection1/conf/schema-minimal.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + 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. +--> +<schema name="minimal" version="1.1"> + <types> + <fieldType name="string" class="solr.StrField"/> + </types> + <fields> + <dynamicField name="*" type="string" indexed="true" stored="true" /> + </fields> +</schema>
