Revert "SENTRY-2208: Refactor out Sentry service into own module from sentry-provider-db (Anthony Young-Garner, reviewed by Sergio Pena, Steve Moist, Na Li)"
This reverts commit b97f5c7aa89d3c9061dd1b6655fc0702caa93f1f. Project: http://git-wip-us.apache.org/repos/asf/sentry/repo Commit: http://git-wip-us.apache.org/repos/asf/sentry/commit/9351d19d Tree: http://git-wip-us.apache.org/repos/asf/sentry/tree/9351d19d Diff: http://git-wip-us.apache.org/repos/asf/sentry/diff/9351d19d Branch: refs/heads/master Commit: 9351d19de9526dc281c7304fe636468bc7afcaff Parents: b97f5c7 Author: Sergio Pena <[email protected]> Authored: Wed May 30 22:26:04 2018 -0500 Committer: Sergio Pena <[email protected]> Committed: Wed May 30 22:26:04 2018 -0500 ---------------------------------------------------------------------- sentry-hdfs/sentry-hdfs-service/pom.xml | 6 +- sentry-provider/sentry-provider-db/pom.xml | 64 +- .../thrift/SentryGenericPolicyProcessor.java | 829 +++ .../SentryGenericPolicyProcessorFactory.java | 44 + .../sentry/api/service/thrift/ConfServlet.java | 71 + .../api/service/thrift/LogLevelServlet.java | 122 + .../api/service/thrift/PubSubServlet.java | 128 + .../api/service/thrift/SentryAdminServlet.java | 132 + .../api/service/thrift/SentryAuthFilter.java | 89 + ...SentryHealthCheckServletContextListener.java | 35 + .../api/service/thrift/SentryMetrics.java | 413 ++ .../SentryMetricsServletContextListener.java | 32 + .../thrift/SentryPolicyStoreProcessor.java | 1239 +++++ .../SentryPolicyStoreProcessorFactory.java | 43 + .../api/service/thrift/SentryWebServer.java | 240 + .../provider/db/SentryPolicyStorePlugin.java | 97 + .../service/persistent/DelegateSentryStore.java | 422 ++ .../service/persistent/PrivilegeObject.java | 231 + .../persistent/PrivilegeOperatePersistence.java | 568 +++ .../service/persistent/SentryStoreLayer.java | 186 + .../tools/GenericPrivilegeConverter.java | 191 + .../tools/TSentryPrivilegeConverter.java | 35 + .../log/appender/AuditLoggerTestAppender.java | 52 + .../RollingFileWithoutDeleteAppender.java | 175 + .../db/log/entity/AuditMetadataLogEntity.java | 155 + .../db/log/entity/DBAuditMetadataLogEntity.java | 124 + .../db/log/entity/GMAuditMetadataLogEntity.java | 97 + .../provider/db/log/entity/JsonLogEntity.java | 25 + .../db/log/entity/JsonLogEntityFactory.java | 351 ++ .../provider/db/log/util/CommandUtil.java | 233 + .../sentry/provider/db/log/util/Constants.java | 116 + .../db/service/model/MAuthzPathsMapping.java | 147 + .../db/service/model/MAuthzPathsSnapshotId.java | 63 + .../sentry/provider/db/service/model/MPath.java | 69 + .../db/service/model/MSentryChange.java | 25 + .../db/service/model/MSentryGMPrivilege.java | 436 ++ .../provider/db/service/model/MSentryGroup.java | 112 + .../service/model/MSentryHmsNotification.java | 79 + .../db/service/model/MSentryPathChange.java | 157 + .../db/service/model/MSentryPermChange.java | 130 + .../db/service/model/MSentryPrivilege.java | 347 ++ .../provider/db/service/model/MSentryRole.java | 224 + .../provider/db/service/model/MSentryUser.java | 154 + .../provider/db/service/model/MSentryUtil.java | 89 + .../db/service/model/MSentryVersion.java | 66 + .../provider/db/service/model/package.jdo | 341 ++ .../db/service/persistent/CounterWait.java | 341 ++ .../persistent/DeltaTransactionBlock.java | 103 + .../persistent/FixedJsonInstanceSerializer.java | 163 + .../db/service/persistent/HAContext.java | 300 ++ .../db/service/persistent/HMSFollower.java | 530 ++ .../service/persistent/LeaderStatusMonitor.java | 286 ++ .../persistent/NotificationProcessor.java | 777 +++ .../db/service/persistent/PathsImage.java | 54 + .../db/service/persistent/PermissionsImage.java | 59 + .../service/persistent/QueryParamBuilder.java | 429 ++ .../db/service/persistent/SentryStore.java | 4797 ++++++++++++++++++ .../persistent/SentryStoreSchemaInfo.java | 143 + .../db/service/persistent/TransactionBlock.java | 39 + .../service/persistent/TransactionManager.java | 260 + .../service/thrift/FullUpdateInitializer.java | 524 ++ .../service/thrift/FullUpdateModifier.java | 606 +++ .../sentry/service/thrift/GSSCallback.java | 110 + .../apache/sentry/service/thrift/HMSClient.java | 56 + .../sentry/service/thrift/HMSFollowerState.java | 43 + .../service/thrift/HiveConnectionFactory.java | 35 + .../service/thrift/HiveNotificationFetcher.java | 211 + .../thrift/HiveSimpleConnectionFactory.java | 129 + .../service/thrift/JaasConfiguration.java | 133 + .../service/thrift/KerberosConfiguration.java | 107 + .../sentry/service/thrift/ProcessorFactory.java | 40 + .../sentry/service/thrift/SentryHMSClient.java | 250 + .../service/thrift/SentryKerberosContext.java | 162 + .../sentry/service/thrift/SentryService.java | 658 +++ .../thrift/SentryServiceClientFactory.java | 111 + .../service/thrift/SentryServiceFactory.java | 27 + .../service/thrift/SentryServiceState.java | 44 + .../sentry/service/thrift/SentryState.java | 27 + .../sentry/service/thrift/SentryStateBank.java | 159 + .../src/main/webapp/SentryService.html | 61 + .../src/main/webapp/css/bootstrap-theme.min.css | 10 + .../src/main/webapp/css/bootstrap.min.css | 9 + .../src/main/webapp/css/sentry.css | 52 + .../src/main/webapp/sentry.png | Bin 0 -> 3223 bytes .../SentryGenericServiceIntegrationBase.java | 73 + .../TestAuditLogForSentryGenericService.java | 296 ++ .../TestSentryGenericPolicyProcessor.java | 364 ++ .../thrift/TestSentryGenericServiceClient.java | 61 + .../TestSentryGenericServiceIntegration.java | 503 ++ .../service/thrift/SentryMiniKdcTestcase.java | 68 + .../TestAuthorizingDDLAuditLogWithKerberos.java | 295 ++ .../thrift/TestConnectionWithTicketTimeout.java | 57 + .../thrift/TestNotificationHandlerInvoker.java | 102 + .../thrift/TestSentryPolicyServiceClient.java | 64 + .../thrift/TestSentryPolicyStoreProcessor.java | 81 + .../TestSentryServerForPoolWithoutKerberos.java | 35 + .../thrift/TestSentryServerLogLevel.java | 100 + .../service/thrift/TestSentryServerPubSub.java | 181 + .../thrift/TestSentryServerWithoutKerberos.java | 214 + .../thrift/TestSentryServiceClientPool.java | 111 + .../thrift/TestSentryServiceFailureCase.java | 75 + .../TestSentryServiceForPoolWithKerberos.java | 35 + .../thrift/TestSentryServiceImportExport.java | 751 +++ .../thrift/TestSentryServiceIntegration.java | 1102 ++++ .../thrift/TestSentryServiceMetrics.java | 86 + .../TestSentryServiceWithInvalidMsgSize.java | 122 + .../thrift/TestSentryServiceWithKerberos.java | 58 + .../thrift/TestSentryWebServerWithKerberos.java | 175 + .../thrift/TestSentryWebServerWithSSL.java | 64 + .../TestSentryWebServerWithoutSecurity.java | 95 + .../persistent/SentryStoreIntegrationBase.java | 91 + .../persistent/TestDelegateSentryStore.java | 182 + .../TestPrivilegeOperatePersistence.java | 1138 +++++ .../persistent/TestSentryGMPrivilege.java | 207 + .../service/persistent/TestSentryRole.java | 542 ++ .../TestRollingFileWithoutDeleteAppender.java | 106 + .../entity/TestDbAuditMetadataLogEntity.java | 69 + .../entity/TestGMAuditMetadataLogEntity.java | 74 + .../db/log/entity/TestJsonLogEntityFactory.java | 272 + .../log/entity/TestJsonLogEntityFactoryGM.java | 259 + .../provider/db/log/util/TestCommandUtil.java | 416 ++ .../db/service/model/TestMSentryUtil.java | 106 + .../db/service/persistent/TestCounterWait.java | 107 + .../db/service/persistent/TestHMSFollower.java | 1296 +++++ .../TestHMSFollowerSentryStoreIntegration.java | 256 + .../persistent/TestLeaderStatusMonitor.java | 200 + .../persistent/TestNotificationProcessor.java | 488 ++ .../service/persistent/TestSentryPrivilege.java | 245 + .../db/service/persistent/TestSentryStore.java | 4036 +++++++++++++++ .../persistent/TestSentryStoreImportExport.java | 1167 +++++ .../TestSentryStoreToAuthorizable.java | 86 + .../service/persistent/TestSentryVersion.java | 91 + .../thrift/SentryServiceIntegrationBase.java | 309 ++ .../thrift/SentryStateBankTestHelper.java | 29 + .../service/thrift/SentryWebMetricParser.java | 86 + .../thrift/TestFullUpdateInitializer.java | 346 ++ .../service/thrift/TestFullUpdateModifier.java | 482 ++ .../thrift/TestHiveNotificationFetcher.java | 163 + .../service/thrift/TestSentryHMSClient.java | 344 ++ .../service/thrift/TestSentryStateBank.java | 84 + sentry-service/pom.xml | 2 - sentry-service/sentry-service-client/pom.xml | 69 - .../thrift/SentryServiceClientFactory.java | 111 - sentry-service/sentry-service-server/pom.xml | 321 -- .../thrift/SentryGenericPolicyProcessor.java | 829 --- .../SentryGenericPolicyProcessorFactory.java | 44 - .../sentry/api/service/thrift/ConfServlet.java | 71 - .../api/service/thrift/LogLevelServlet.java | 122 - .../api/service/thrift/PubSubServlet.java | 128 - .../api/service/thrift/SentryAdminServlet.java | 132 - .../api/service/thrift/SentryAuthFilter.java | 89 - ...SentryHealthCheckServletContextListener.java | 35 - .../api/service/thrift/SentryMetrics.java | 413 -- .../SentryMetricsServletContextListener.java | 32 - .../thrift/SentryPolicyStoreProcessor.java | 1239 ----- .../SentryPolicyStoreProcessorFactory.java | 43 - .../api/service/thrift/SentryWebServer.java | 240 - .../provider/db/SentryPolicyStorePlugin.java | 97 - .../service/persistent/DelegateSentryStore.java | 422 -- .../service/persistent/PrivilegeObject.java | 231 - .../persistent/PrivilegeOperatePersistence.java | 566 --- .../service/persistent/SentryStoreLayer.java | 186 - .../tools/GenericPrivilegeConverter.java | 191 - .../tools/TSentryPrivilegeConverter.java | 35 - .../log/appender/AuditLoggerTestAppender.java | 52 - .../RollingFileWithoutDeleteAppender.java | 175 - .../db/log/entity/AuditMetadataLogEntity.java | 155 - .../db/log/entity/DBAuditMetadataLogEntity.java | 124 - .../db/log/entity/GMAuditMetadataLogEntity.java | 97 - .../provider/db/log/entity/JsonLogEntity.java | 25 - .../db/log/entity/JsonLogEntityFactory.java | 351 -- .../provider/db/log/util/CommandUtil.java | 233 - .../sentry/provider/db/log/util/Constants.java | 116 - .../db/service/model/MAuthzPathsMapping.java | 147 - .../db/service/model/MAuthzPathsSnapshotId.java | 63 - .../sentry/provider/db/service/model/MPath.java | 69 - .../db/service/model/MSentryChange.java | 25 - .../db/service/model/MSentryGMPrivilege.java | 436 -- .../provider/db/service/model/MSentryGroup.java | 112 - .../service/model/MSentryHmsNotification.java | 79 - .../db/service/model/MSentryPathChange.java | 157 - .../db/service/model/MSentryPermChange.java | 130 - .../db/service/model/MSentryPrivilege.java | 347 -- .../provider/db/service/model/MSentryRole.java | 224 - .../provider/db/service/model/MSentryUser.java | 154 - .../provider/db/service/model/MSentryUtil.java | 89 - .../db/service/model/MSentryVersion.java | 66 - .../provider/db/service/model/package.jdo | 341 -- .../db/service/persistent/CounterWait.java | 341 -- .../persistent/DeltaTransactionBlock.java | 103 - .../persistent/FixedJsonInstanceSerializer.java | 163 - .../db/service/persistent/HAContext.java | 300 -- .../db/service/persistent/HMSFollower.java | 530 -- .../service/persistent/LeaderStatusMonitor.java | 286 -- .../persistent/NotificationProcessor.java | 777 --- .../db/service/persistent/PathsImage.java | 54 - .../db/service/persistent/PermissionsImage.java | 59 - .../service/persistent/QueryParamBuilder.java | 429 -- .../db/service/persistent/SentryStore.java | 4796 ----------------- .../persistent/SentryStoreSchemaInfo.java | 143 - .../db/service/persistent/TransactionBlock.java | 39 - .../service/persistent/TransactionManager.java | 260 - .../service/thrift/FullUpdateInitializer.java | 524 -- .../service/thrift/FullUpdateModifier.java | 606 --- .../sentry/service/thrift/GSSCallback.java | 110 - .../apache/sentry/service/thrift/HMSClient.java | 56 - .../sentry/service/thrift/HMSFollowerState.java | 43 - .../service/thrift/HiveConnectionFactory.java | 35 - .../service/thrift/HiveNotificationFetcher.java | 211 - .../thrift/HiveSimpleConnectionFactory.java | 129 - .../service/thrift/JaasConfiguration.java | 133 - .../service/thrift/KerberosConfiguration.java | 107 - .../sentry/service/thrift/ProcessorFactory.java | 40 - .../sentry/service/thrift/SentryHMSClient.java | 250 - .../service/thrift/SentryKerberosContext.java | 162 - .../sentry/service/thrift/SentryService.java | 658 --- .../service/thrift/SentryServiceFactory.java | 27 - .../service/thrift/SentryServiceState.java | 44 - .../sentry/service/thrift/SentryState.java | 27 - .../sentry/service/thrift/SentryStateBank.java | 159 - .../src/main/webapp/SentryService.html | 61 - .../src/main/webapp/css/bootstrap-theme.min.css | 10 - .../src/main/webapp/css/bootstrap.min.css | 9 - .../src/main/webapp/css/sentry.css | 52 - .../src/main/webapp/sentry.png | Bin 3223 -> 0 bytes .../SentryGenericServiceIntegrationBase.java | 73 - .../TestAuditLogForSentryGenericService.java | 296 -- .../TestSentryGenericPolicyProcessor.java | 364 -- .../thrift/TestSentryGenericServiceClient.java | 62 - .../TestSentryGenericServiceIntegration.java | 503 -- .../service/thrift/SentryMiniKdcTestcase.java | 68 - .../TestAuthorizingDDLAuditLogWithKerberos.java | 295 -- .../thrift/TestConnectionWithTicketTimeout.java | 57 - .../thrift/TestNotificationHandlerInvoker.java | 102 - .../thrift/TestSentryPolicyServiceClient.java | 64 - .../thrift/TestSentryPolicyStoreProcessor.java | 81 - .../TestSentryServerForPoolWithoutKerberos.java | 35 - .../thrift/TestSentryServerLogLevel.java | 100 - .../service/thrift/TestSentryServerPubSub.java | 181 - .../thrift/TestSentryServerWithoutKerberos.java | 214 - .../thrift/TestSentryServiceClientPool.java | 111 - .../thrift/TestSentryServiceFailureCase.java | 75 - .../TestSentryServiceForPoolWithKerberos.java | 35 - .../thrift/TestSentryServiceImportExport.java | 751 --- .../thrift/TestSentryServiceIntegration.java | 1102 ---- .../thrift/TestSentryServiceMetrics.java | 86 - .../TestSentryServiceWithInvalidMsgSize.java | 122 - .../thrift/TestSentryServiceWithKerberos.java | 58 - .../thrift/TestSentryWebServerWithKerberos.java | 175 - .../thrift/TestSentryWebServerWithSSL.java | 64 - .../TestSentryWebServerWithoutSecurity.java | 95 - .../TestSentryWebServiceForAuthTypeNone.java | 64 - .../persistent/SentryStoreIntegrationBase.java | 91 - .../persistent/TestDelegateSentryStore.java | 182 - .../TestPrivilegeOperatePersistence.java | 1138 ----- .../persistent/TestSentryGMPrivilege.java | 207 - .../service/persistent/TestSentryRole.java | 542 -- .../TestRollingFileWithoutDeleteAppender.java | 106 - .../entity/TestDbAuditMetadataLogEntity.java | 69 - .../entity/TestGMAuditMetadataLogEntity.java | 74 - .../db/log/entity/TestJsonLogEntityFactory.java | 272 - .../log/entity/TestJsonLogEntityFactoryGM.java | 259 - .../provider/db/log/util/TestCommandUtil.java | 416 -- .../db/service/model/TestMSentryUtil.java | 106 - .../db/service/persistent/TestCounterWait.java | 107 - .../db/service/persistent/TestHMSFollower.java | 1296 ----- .../TestHMSFollowerSentryStoreIntegration.java | 256 - .../persistent/TestLeaderStatusMonitor.java | 200 - .../persistent/TestNotificationProcessor.java | 488 -- .../service/persistent/TestSentryPrivilege.java | 245 - .../db/service/persistent/TestSentryStore.java | 4036 --------------- .../persistent/TestSentryStoreImportExport.java | 1167 ----- .../TestSentryStoreToAuthorizable.java | 86 - .../service/persistent/TestSentryVersion.java | 91 - .../TestSentryWebServiceForAuthTypeNone.java | 64 + .../thrift/SentryServiceIntegrationBase.java | 309 -- .../thrift/SentryStateBankTestHelper.java | 29 - .../service/thrift/SentryWebMetricParser.java | 86 - .../thrift/TestFullUpdateInitializer.java | 346 -- .../service/thrift/TestFullUpdateModifier.java | 482 -- .../thrift/TestHiveNotificationFetcher.java | 163 - .../service/thrift/TestSentryHMSClient.java | 344 -- .../service/thrift/TestSentryStateBank.java | 84 - .../src/test/resources/cacerts.jks | 0 .../src/test/resources/keystore.jks | 0 .../src/test/resources/log4j.properties | 34 - sentry-tests/sentry-tests-hive/pom.xml | 3 +- sentry-tests/sentry-tests-kafka/pom.xml | 3 +- sentry-tests/sentry-tests-solr/pom.xml | 53 +- sentry-tests/sentry-tests-sqoop/pom.xml | 3 +- sentry-tools/pom.xml | 7 +- 291 files changed, 39337 insertions(+), 39762 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/sentry/blob/9351d19d/sentry-hdfs/sentry-hdfs-service/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-hdfs/sentry-hdfs-service/pom.xml b/sentry-hdfs/sentry-hdfs-service/pom.xml index 888f61e..50a451d 100644 --- a/sentry-hdfs/sentry-hdfs-service/pom.xml +++ b/sentry-hdfs/sentry-hdfs-service/pom.xml @@ -73,8 +73,7 @@ limitations under the License. </dependency> <dependency> <groupId>org.apache.sentry</groupId> - <artifactId>sentry-service-server</artifactId> - <version>${project.version}</version> + <artifactId>sentry-provider-db</artifactId> </dependency> <dependency> <groupId>org.apache.hive</groupId> @@ -106,8 +105,7 @@ limitations under the License. </dependency> <dependency> <groupId>org.apache.sentry</groupId> - <artifactId>sentry-service-server</artifactId> - <version>${project.version}</version> + <artifactId>sentry-provider-db</artifactId> <type>test-jar</type> <scope>test</scope> </dependency> http://git-wip-us.apache.org/repos/asf/sentry/blob/9351d19d/sentry-provider/sentry-provider-db/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/pom.xml b/sentry-provider/sentry-provider-db/pom.xml index a8a15bf..48a187a 100644 --- a/sentry-provider/sentry-provider-db/pom.xml +++ b/sentry-provider/sentry-provider-db/pom.xml @@ -154,11 +154,6 @@ limitations under the License. <version>${project.version}</version> </dependency> <dependency> - <groupId>org.apache.sentry</groupId> - <artifactId>sentry-service-client</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-shims</artifactId> <scope>provided</scope> @@ -292,6 +287,65 @@ limitations under the License. </resources> <plugins> <plugin> + <groupId>com.google.code.maven-replacer-plugin</groupId> + <artifactId>replacer</artifactId> + <version>1.5.2</version> + <executions> + <execution> + <id>replaceTokens</id> + <phase>clean</phase> + <goals> + <goal>replace</goal> + </goals> + </execution> + </executions> + <configuration> + <file>${basedir}/src/main/webapp/SentryService.html</file> + <replacements> + <replacement> + <token>%PROJECT_VERSION%</token> + <value>${project.version}</value> + </replacement> + </replacements> + </configuration> + </plugin> + <plugin> + <groupId>org.datanucleus</groupId> + <artifactId>datanucleus-maven-plugin</artifactId> + <version>${datanucleus.maven.plugin.version}</version> + <configuration> + <api>JDO</api> + <metadataIncludes>**/*.jdo</metadataIncludes> + <verbose>true</verbose> + </configuration> + <executions> + <execution> + <phase>process-classes</phase> + <goals> + <goal>enhance</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.datanucleus</groupId> + <artifactId>datanucleus-core</artifactId> + <version>${datanucleus-core.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> http://git-wip-us.apache.org/repos/asf/sentry/blob/9351d19d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/generic/thrift/SentryGenericPolicyProcessor.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/generic/thrift/SentryGenericPolicyProcessor.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/generic/thrift/SentryGenericPolicyProcessor.java new file mode 100644 index 0000000..1cc4b1b --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/generic/thrift/SentryGenericPolicyProcessor.java @@ -0,0 +1,829 @@ +/** + * 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.api.generic.thrift; + +import static org.apache.sentry.core.common.utils.SentryConstants.AUTHORIZABLE_JOINER; +import static org.apache.sentry.core.common.utils.SentryConstants.KV_JOINER; + +import java.lang.reflect.Constructor; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.api.common.ThriftConstants; +import org.apache.sentry.core.common.exception.SentryUserException; +import org.apache.sentry.core.common.Authorizable; +import org.apache.sentry.core.common.utils.SentryConstants; +import org.apache.sentry.core.common.exception.SentrySiteConfigurationException; +import org.apache.sentry.core.model.db.AccessConstants; +import org.apache.sentry.core.common.utils.KeyValue; +import org.apache.sentry.provider.common.AuthorizationComponent; +import org.apache.sentry.core.common.exception.SentryAccessDeniedException; +import org.apache.sentry.core.common.exception.SentryAlreadyExistsException; +import org.apache.sentry.core.common.exception.SentryInvalidInputException; +import org.apache.sentry.core.common.exception.SentryNoSuchObjectException; +import org.apache.sentry.core.common.exception.SentryThriftAPIMismatchException; +import org.apache.sentry.provider.db.generic.service.persistent.DelegateSentryStore; +import org.apache.sentry.provider.db.generic.service.persistent.PrivilegeObject; +import org.apache.sentry.provider.db.generic.service.persistent.PrivilegeObject.Builder; +import org.apache.sentry.provider.db.generic.service.persistent.SentryStoreLayer; +import org.apache.sentry.provider.db.log.entity.JsonLogEntityFactory; +import org.apache.sentry.provider.db.log.util.Constants; +import org.apache.sentry.provider.db.service.model.MSentryGMPrivilege; +import org.apache.sentry.provider.db.service.model.MSentryRole; +import org.apache.sentry.core.common.utils.PolicyStoreConstants; +import org.apache.sentry.api.service.thrift.SentryPolicyStoreProcessor; +import org.apache.sentry.service.common.ServiceConstants.ServerConfig; +import org.apache.sentry.api.common.Status; +import org.apache.sentry.service.thrift.TSentryResponseStatus; +import org.apache.thrift.TException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +public class SentryGenericPolicyProcessor implements SentryGenericPolicyService.Iface { + private static final Logger LOGGER = LoggerFactory.getLogger(SentryGenericPolicyProcessor.class); + private static final Logger AUDIT_LOGGER = LoggerFactory + .getLogger(Constants.AUDIT_LOGGER_NAME_GENERIC); + private final Configuration conf; + private final ImmutableSet<String> adminGroups; + private final SentryStoreLayer store; + private final NotificationHandlerInvoker handerInvoker; + + private static final String ACCESS_DENIAL_MESSAGE = "Access denied to "; + + SentryGenericPolicyProcessor(Configuration conf) throws Exception { + this.store = new DelegateSentryStore(conf); + this.handerInvoker = new NotificationHandlerInvoker(createHandlers(conf)); + this.conf = conf; + adminGroups = ImmutableSet.copyOf((Sets.newHashSet(conf.getStrings( + ServerConfig.ADMIN_GROUPS, new String[]{})))); + } + + @VisibleForTesting + SentryGenericPolicyProcessor(Configuration conf, SentryStoreLayer store) throws Exception { + this.store = store; + this.handerInvoker = new NotificationHandlerInvoker(createHandlers(conf)); + this.conf = conf; + adminGroups = ImmutableSet.copyOf(toTrimmed(Sets.newHashSet(conf.getStrings( + ServerConfig.ADMIN_GROUPS, new String[]{})))); + } + + private void authorize(String requestorUser, Set<String> requestorGroups) + throws SentryAccessDeniedException { + if (!inAdminGroups(requestorGroups)) { + String msg = "User: " + requestorUser + " is part of " + requestorGroups + + " which does not, intersect admin groups " + adminGroups; + LOGGER.warn(msg); + throw new SentryAccessDeniedException(ACCESS_DENIAL_MESSAGE + requestorUser); + } + } + + private Set<String> toTrimmedLower(Set<String> s) { + if (s == null) { + return Collections.emptySet(); + } + Set<String> result = new HashSet<>(s.size()); + for (String v : s) { + result.add(v.trim().toLowerCase()); + } + return result; + } + + private Set<String> toTrimmed(Set<String> s) { + if (s == null) { + return Collections.emptySet(); + } + Set<String> result = new HashSet<>(s.size()); + for (String v : s) { + result.add(v.trim()); + } + return result; + } + + private String toTrimmedLower(String s) { + if (Strings.isNullOrEmpty(s)){ + return ""; + } + return s.trim().toLowerCase(); + } + + private static Set<String> getRequestorGroups(Configuration conf, String userName) throws SentryUserException { + return SentryPolicyStoreProcessor.getGroupsFromUserName(conf, userName); + } + + private boolean inAdminGroups(Set<String> requestorGroups) { + return !Sets.intersection(adminGroups, requestorGroups).isEmpty(); + } + + static List<NotificationHandler> createHandlers(Configuration conf) throws SentrySiteConfigurationException { + + List<NotificationHandler> handlers = Lists.newArrayList(); + Iterable<String> notificationHandlers = Splitter.onPattern("[\\s,]").trimResults() + .omitEmptyStrings().split(conf.get(PolicyStoreConstants.SENTRY_GENERIC_POLICY_NOTIFICATION, "")); + try { + for (String notificationHandler : notificationHandlers) { + handlers.add(createInstance(notificationHandler, conf, NotificationHandler.class)); + } + } catch (Exception e) { + throw new SentrySiteConfigurationException("Create notificationHandlers error: " + e.getMessage(), e); + } + return handlers; + } + + @SuppressWarnings("unchecked") + private static <T> T createInstance(String className, Configuration conf, Class<T> iface) throws Exception { + T result; + try { + Class<?> clazz = Class.forName(className); + if (!iface.isAssignableFrom(clazz)) { + throw new IllegalArgumentException("Class " + clazz + " is not a " + + iface.getName()); + } + Constructor<T> meth = (Constructor<T>)clazz.getDeclaredConstructor(Configuration.class); + meth.setAccessible(true); + result = meth.newInstance(new Object[]{conf}); + } catch (Exception e) { + throw new RuntimeException(e); + } + return result; + } + + private <T> Response<T> requestHandle(RequestHandler<T> handler) { + Response<T> response = new Response<T>(); + try { + response = handler.handle(); + } catch (SentryAccessDeniedException e) { + String msg = "Sentry access denied: " + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.AccessDenied(e.getMessage(), e); + } catch (SentryAlreadyExistsException e) { + String msg = "Sentry object already exists: " + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.AlreadyExists(e.getMessage(), e); + } catch (SentryNoSuchObjectException e) { + String msg = "Sentry object doesn't exist: " + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.NoSuchObject(e.getMessage(), e); + } catch (SentryInvalidInputException e) { + String msg = "Invalid input privilege object: " + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.InvalidInput(msg, e); + } catch (SentryThriftAPIMismatchException e) { + String msg = "Sentry thrift API mismatch error: " + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.THRIFT_VERSION_MISMATCH(e.getMessage(), e); + } catch (Exception e) { + String msg = "Unknown error:" + e.getMessage(); + LOGGER.error(msg, e); + response.status = Status.RuntimeError(msg, e); + } + return response; + } + + private PrivilegeObject toPrivilegeObject(TSentryPrivilege tSentryPrivilege) { + Boolean grantOption; + if (tSentryPrivilege.getGrantOption().equals(TSentryGrantOption.TRUE)) { + grantOption = true; + } else if (tSentryPrivilege.getGrantOption().equals(TSentryGrantOption.FALSE)) { + grantOption = false; + } else { + grantOption = null; + } + return new Builder().setComponent(tSentryPrivilege.getComponent()) + .setService(tSentryPrivilege.getServiceName()) + .setAuthorizables(toAuthorizables(tSentryPrivilege.getAuthorizables())) + .setAction(tSentryPrivilege.getAction()) + .withGrantOption(grantOption) + .build(); + } + + private TSentryPrivilege fromPrivilegeObject(PrivilegeObject privilege) { + + TSentryPrivilege tPrivilege = new TSentryPrivilege(privilege.getComponent(), privilege.getService(), + fromAuthorizable(privilege.getAuthorizables()), + privilege.getAction()); + if (privilege.getGrantOption() == null) { + tPrivilege.setGrantOption(TSentryGrantOption.UNSET); + } else if (privilege.getGrantOption()) { + tPrivilege.setGrantOption(TSentryGrantOption.TRUE); + } else { + tPrivilege.setGrantOption(TSentryGrantOption.FALSE); + } + return tPrivilege; + } + + private List<TAuthorizable> fromAuthorizable(List<? extends Authorizable> authorizables) { + List<TAuthorizable> tAuthorizables = Lists.newArrayList(); + for (Authorizable authorizable : authorizables) { + tAuthorizables.add(new TAuthorizable(authorizable.getTypeName(), authorizable.getName())); + } + return tAuthorizables; + } + + private String fromAuthorizableToStr(List<? extends Authorizable> authorizables) { + if (authorizables != null && !authorizables.isEmpty()) { + List<String> privileges = Lists.newArrayList(); + + for (Authorizable authorizable : authorizables) { + + privileges.add(SentryConstants.KV_JOINER.join(authorizable.getTypeName(), + authorizable.getName())); + } + + return SentryConstants.AUTHORIZABLE_JOINER.join(privileges); + } else { + return ""; + } + } + + private List<? extends Authorizable> toAuthorizables(List<TAuthorizable> tAuthorizables) { + List<Authorizable> authorizables = Lists.newArrayList(); + if (tAuthorizables == null) { + return authorizables; + } + for (final TAuthorizable tAuthorizable : tAuthorizables) { + authorizables.add(new Authorizable() { + @Override + public String getTypeName() { + return tAuthorizable.getType(); + } + @Override + public String getName() { + return tAuthorizable.getName(); + } + }); + } + return authorizables; + } + + private List<? extends Authorizable> toAuthorizables(String privilegeStr) { + List<Authorizable> authorizables = Lists.newArrayList(); + if (privilegeStr == null) { + return authorizables; + } + + for (String authorizable : SentryConstants.AUTHORIZABLE_SPLITTER.split(privilegeStr)) { + KeyValue tempKV = new KeyValue(authorizable); + final String key = tempKV.getKey(); + final String value = tempKV.getValue(); + + authorizables.add(new Authorizable() { + @Override + public String getTypeName() { + return key; + } + + @Override + public String getName() { + return value; + } + }); + } + + return authorizables; + } + + // Construct the role to set of privileges mapping based on the + // MSentryGMPrivilege information. + private TSentryPrivilegeMap toTSentryPrivilegeMap(Set<MSentryGMPrivilege> mPrivileges) { + + // Mapping of <Role, Set<Privilege>>. + Map<String, Set<TSentryPrivilege>> tPrivilegeMap = Maps.newTreeMap(); + + for (MSentryGMPrivilege mPrivilege : mPrivileges) { + for (MSentryRole role : mPrivilege.getRoles()) { + + TSentryPrivilege tPrivilege = toTSentryPrivilege(mPrivilege); + + if (tPrivilegeMap.containsKey(role.getRoleName())) { + tPrivilegeMap.get(role.getRoleName()).add(tPrivilege); + } else { + Set<TSentryPrivilege> tPrivilegeSet = Sets.newTreeSet(); + tPrivilegeSet.add(tPrivilege); + tPrivilegeMap.put(role.getRoleName(), tPrivilegeSet); + } + } + } + + return new TSentryPrivilegeMap(tPrivilegeMap); + } + + // Construct TSentryPrivilege based on MSentryGMPrivilege information. + private TSentryPrivilege toTSentryPrivilege(MSentryGMPrivilege mPrivilege) { + + TSentryPrivilege tPrivilege = new TSentryPrivilege(mPrivilege.getComponentName(), + mPrivilege.getServiceName(), fromAuthorizable(mPrivilege.getAuthorizables()), mPrivilege.getAction()); + + if (mPrivilege.getGrantOption() == null) { + tPrivilege.setGrantOption(TSentryGrantOption.UNSET); + } else if (mPrivilege.getGrantOption()) { + tPrivilege.setGrantOption(TSentryGrantOption.TRUE); + } else { + tPrivilege.setGrantOption(TSentryGrantOption.FALSE); + } + + return tPrivilege; + } + + private Set<String> buildPermissions(Set<PrivilegeObject> privileges) { + Set<String> permissions = Sets.newHashSet(); + for (PrivilegeObject privilege : privileges) { + List<String> hierarchy = Lists.newArrayList(); + if (hasComponentServerPrivilege(privilege.getComponent())) { + hierarchy.add(KV_JOINER.join("server", privilege.getService())); + } + for (Authorizable authorizable : privilege.getAuthorizables()) { + hierarchy.add(KV_JOINER.join(authorizable.getTypeName(),authorizable.getName())); + } + hierarchy.add(KV_JOINER.join("action", privilege.getAction())); + permissions.add(AUTHORIZABLE_JOINER.join(hierarchy)); + } + return permissions; + } + + private boolean hasComponentServerPrivilege(String component) { + //judge the component whether has the server privilege, for example: sqoop has the privilege on the server + return AuthorizationComponent.SQOOP.equalsIgnoreCase(component); + } + + @Override + public TCreateSentryRoleResponse create_sentry_role( + final TCreateSentryRoleRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + store.createRole(request.getComponent(), request.getRoleName(), + request.getRequestorUserName()); + return new Response<Void>(Status.OK()); + } + }); + + TCreateSentryRoleResponse tResponse = new TCreateSentryRoleResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.create_sentry_role(request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for create role: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TDropSentryRoleResponse drop_sentry_role(final TDropSentryRoleRequest request) + throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + store.dropRole(request.getComponent(), request.getRoleName(), + request.getRequestorUserName()); + return new Response<Void>(Status.OK()); + } + }); + + TDropSentryRoleResponse tResponse = new TDropSentryRoleResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.drop_sentry_role(request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for drop role: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TAlterSentryRoleGrantPrivilegeResponse alter_sentry_role_grant_privilege( + final TAlterSentryRoleGrantPrivilegeRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + store.alterRoleGrantPrivilege(request.getComponent(), + request.getRoleName(), + toPrivilegeObject(request.getPrivilege()), + request.getRequestorUserName()); + return new Response<Void>(Status.OK()); + } + }); + + TAlterSentryRoleGrantPrivilegeResponse tResponse = new TAlterSentryRoleGrantPrivilegeResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.alter_sentry_role_grant_privilege(request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for grant privilege to role: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TAlterSentryRoleRevokePrivilegeResponse alter_sentry_role_revoke_privilege( + final TAlterSentryRoleRevokePrivilegeRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + store.alterRoleRevokePrivilege(request.getComponent(), + request.getRoleName(), + toPrivilegeObject(request.getPrivilege()), + request.getRequestorUserName()); + return new Response<Void>(Status.OK()); + } + }); + + TAlterSentryRoleRevokePrivilegeResponse tResponse = + new TAlterSentryRoleRevokePrivilegeResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.alter_sentry_role_revoke_privilege(request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for revoke privilege from role: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TAlterSentryRoleAddGroupsResponse alter_sentry_role_add_groups( + final TAlterSentryRoleAddGroupsRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + store.alterRoleAddGroups(request.getComponent(), + request.getRoleName(), + request.getGroups(), + request.getRequestorUserName()); + return new Response<Void>(Status.OK()); + } + }); + + TAlterSentryRoleAddGroupsResponse tResponse = + new TAlterSentryRoleAddGroupsResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.alter_sentry_role_add_groups(request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for add role to group: " + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TAlterSentryRoleDeleteGroupsResponse alter_sentry_role_delete_groups( + final TAlterSentryRoleDeleteGroupsRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + store.alterRoleDeleteGroups(request.getComponent(), + request.getRoleName(), + request.getGroups(), + request.getRequestorUserName()); + return new Response<Void>(Status.OK()); + } + }); + + TAlterSentryRoleDeleteGroupsResponse tResponse = + new TAlterSentryRoleDeleteGroupsResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.alter_sentry_role_delete_groups(request, tResponse); + } + + try { + AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance() + .createJsonLogEntity(request, tResponse, conf).toJsonFormatLog()); + } catch (Exception e) { + // if any exception, log the exception. + String msg = "Error in creating audit log for delete role from group: " + + e.getMessage(); + LOGGER.error(msg, e); + } + return tResponse; + } + + @Override + public TListSentryRolesResponse list_sentry_roles_by_group( + final TListSentryRolesRequest request) throws TException { + Response<Set<TSentryRole>> respose = requestHandle(new RequestHandler<Set<TSentryRole>>() { + @Override + public Response<Set<TSentryRole>> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + Set<String> groups = getRequestorGroups(conf, request.getRequestorUserName()); + if (!AccessConstants.ALL.equalsIgnoreCase(request.getGroupName())) { + boolean admin = inAdminGroups(groups); + //Only admin users can list all roles in the system ( groupname = null) + //Non admin users are only allowed to list only groups which they belong to + if(!admin && (request.getGroupName() == null || !groups.contains(request.getGroupName()))) { + throw new SentryAccessDeniedException(ACCESS_DENIAL_MESSAGE + request.getRequestorUserName()); + } + groups.clear(); + groups.add(request.getGroupName()); + } + + Set<String> roleNames = store.getRolesByGroups(request.getComponent(), groups); + Set<TSentryRole> tSentryRoles = Sets.newHashSet(); + for (String roleName : roleNames) { + Set<String> groupsForRoleName = store.getGroupsByRoles(request.getComponent(), Sets.newHashSet(roleName)); + tSentryRoles.add(new TSentryRole(roleName, groupsForRoleName)); + } + return new Response<Set<TSentryRole>>(Status.OK(), tSentryRoles); + } + }); + TListSentryRolesResponse tResponse = new TListSentryRolesResponse(); + tResponse.setStatus(respose.status); + tResponse.setRoles(respose.content); + return tResponse; + } + + @Override + public TListSentryPrivilegesResponse list_sentry_privileges_by_role( + final TListSentryPrivilegesRequest request) throws TException { + Response<Set<TSentryPrivilege>> respose = requestHandle(new RequestHandler<Set<TSentryPrivilege>>() { + @Override + public Response<Set<TSentryPrivilege>> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + Set<String> groups = getRequestorGroups(conf, request.getRequestorUserName()); + if (!inAdminGroups(groups)) { + Set<String> roleNamesForGroups = toTrimmedLower(store.getRolesByGroups(request.getComponent(), groups)); + if (!roleNamesForGroups.contains(toTrimmedLower(request.getRoleName()))) { + throw new SentryAccessDeniedException(ACCESS_DENIAL_MESSAGE + request.getRequestorUserName()); + } + } + Set<PrivilegeObject> privileges = store.getPrivilegesByProvider(request.getComponent(), + request.getServiceName(), + Sets.newHashSet(request.getRoleName()), + null, toAuthorizables(request.getAuthorizables())); + Set<TSentryPrivilege> tSentryPrivileges = Sets.newHashSet(); + for (PrivilegeObject privilege : privileges) { + tSentryPrivileges.add(fromPrivilegeObject(privilege)); + } + return new Response<Set<TSentryPrivilege>>(Status.OK(), tSentryPrivileges); + } + }); + TListSentryPrivilegesResponse tResponse = new TListSentryPrivilegesResponse(); + tResponse.setStatus(respose.status); + tResponse.setPrivileges(respose.content); + return tResponse; + } + + @Override + public TListSentryPrivilegesForProviderResponse list_sentry_privileges_for_provider( + final TListSentryPrivilegesForProviderRequest request) throws TException { + Response<Set<String>> respose = requestHandle(new RequestHandler<Set<String>>() { + @Override + public Response<Set<String>> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + Set<String> activeRoleNames = toTrimmedLower(request.getRoleSet().getRoles()); + Set<String> roleNamesForGroups = store.getRolesByGroups(request.getComponent(), request.getGroups()); + Set<String> rolesToQuery = request.getRoleSet().isAll() ? roleNamesForGroups : Sets.intersection(activeRoleNames, roleNamesForGroups); + Set<PrivilegeObject> privileges = store.getPrivilegesByProvider(request.getComponent(), + request.getServiceName(), + rolesToQuery, null, + toAuthorizables(request.getAuthorizables())); + return new Response<Set<String>>(Status.OK(), buildPermissions(privileges)); + } + }); + TListSentryPrivilegesForProviderResponse tResponse = new TListSentryPrivilegesForProviderResponse(); + tResponse.setStatus(respose.status); + tResponse.setPrivileges(respose.content); + return tResponse; + } + + @Override + public TListSentryPrivilegesByAuthResponse list_sentry_privileges_by_authorizable(TListSentryPrivilegesByAuthRequest request) throws TException { + + TListSentryPrivilegesByAuthResponse response = new TListSentryPrivilegesByAuthResponse(); + Map<String, TSentryPrivilegeMap> authRoleMap = Maps.newHashMap(); + + // Group names are case sensitive. + Set<String> requestedGroups = request.getGroups(); + String subject = request.getRequestorUserName(); + TSentryActiveRoleSet activeRoleSet = request.getRoleSet(); + Set<String> validActiveRoles = Sets.newHashSet(); + + try { + validateClientVersion(request.getProtocol_version()); + Set<String> memberGroups = getRequestorGroups(conf, subject); + + // Disallow non-admin users to lookup groups that + // they are not part of. + if(!inAdminGroups(memberGroups)) { + + if (requestedGroups != null && !requestedGroups.isEmpty()) { + for (String requestedGroup : requestedGroups) { + + // If user doesn't belong to one of the requested groups, + // then raise security exception. + if (!memberGroups.contains(requestedGroup)) { + throw new SentryAccessDeniedException(ACCESS_DENIAL_MESSAGE + subject); + } + } + } else { + // Non-admin's search is limited to its own groups. + requestedGroups = memberGroups; + } + + Set<String> grantedRoles = toTrimmedLower(store.getRolesByGroups(request.getComponent(), requestedGroups)); + + // If activeRoleSet is not null, disallow non-admin to lookup roles that they are not part of. + if (activeRoleSet != null && !activeRoleSet.isAll()) { + + Set<String> activeRoleNames = toTrimmedLower(activeRoleSet.getRoles()); + for (String activeRole : activeRoleNames) { + if (!grantedRoles.contains(activeRole)) { + throw new SentryAccessDeniedException(ACCESS_DENIAL_MESSAGE + + subject); + } + } + + // For non-admin, valid active roles are intersection of active roles and granted roles. + validActiveRoles.addAll(activeRoleSet.isAll() ? grantedRoles : Sets.intersection(activeRoleNames, grantedRoles)); + } else { + // For non-admin, if activeRoleSet is null, valid active roles would be the granted roles. + validActiveRoles.addAll(grantedRoles); + } + } else { + // For admin, if requestedGroups are empty, requested roles will be all roles. + Set<String> requestedRoles = toTrimmedLower(store.getAllRoleNames()); + if (requestedGroups != null && !requestedGroups.isEmpty()) { + requestedRoles = toTrimmedLower(store.getRolesByGroups(request.getComponent(), requestedGroups)); + } + + // If activeRoleSet (which is optional) is not null, valid active role will be intersection + // of active roles and requested roles. Otherwise, valid active roles are the requested roles. + if (activeRoleSet != null && !activeRoleSet.isAll()) { + validActiveRoles.addAll(Sets.intersection(toTrimmedLower(activeRoleSet.getRoles()), requestedRoles)); + } else { + validActiveRoles.addAll(requestedRoles); + } + } + + // If user is not part of any group.. return empty response + if (request.getAuthorizablesSet() != null) { + for (String authorizablesStr : request.getAuthorizablesSet()) { + + List<? extends Authorizable> authorizables = toAuthorizables(authorizablesStr); + Set<MSentryGMPrivilege> sentryPrivileges = store.getPrivilegesByAuthorizable(request.getComponent(), request.getServiceName(), validActiveRoles, authorizables); + authRoleMap.put(fromAuthorizableToStr(authorizables), toTSentryPrivilegeMap(sentryPrivileges)); + } + } + + response.setPrivilegesMapByAuth(authRoleMap); + response.setStatus(Status.OK()); + } catch (SentryAccessDeniedException e) { + LOGGER.error(e.getMessage(), e); + response.setStatus(Status.AccessDenied(e.getMessage(), e)); + } catch (SentryThriftAPIMismatchException e) { + LOGGER.error(e.getMessage(), e); + response.setStatus(Status.THRIFT_VERSION_MISMATCH(e.getMessage(), e)); + } catch (Exception e) { + String msg = "Unknown error for request: " + request + ", message: " + + e.getMessage(); + LOGGER.error(msg, e); + response.setStatus(Status.RuntimeError(msg, e)); + } + + return response; + } + + @Override + public TDropPrivilegesResponse drop_sentry_privilege( + final TDropPrivilegesRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + store.dropPrivilege(request.getComponent(), + toPrivilegeObject(request.getPrivilege()), + request.getRequestorUserName()); + return new Response<Void>(Status.OK()); + } + }); + + TDropPrivilegesResponse tResponse = new TDropPrivilegesResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.drop_sentry_privilege(request, tResponse); + } + return tResponse; + } + + @Override + public TRenamePrivilegesResponse rename_sentry_privilege( + final TRenamePrivilegesRequest request) throws TException { + Response<Void> respose = requestHandle(new RequestHandler<Void>() { + @Override + public Response<Void> handle() throws Exception { + validateClientVersion(request.getProtocol_version()); + authorize(request.getRequestorUserName(), + getRequestorGroups(conf, request.getRequestorUserName())); + store.renamePrivilege(request.getComponent(), request.getServiceName(), + toAuthorizables(request.getOldAuthorizables()), + toAuthorizables(request.getNewAuthorizables()), + request.getRequestorUserName()); + return new Response<Void>(Status.OK()); + } + }); + + TRenamePrivilegesResponse tResponse = new TRenamePrivilegesResponse(respose.status); + if (Status.OK.getCode() == respose.status.getValue()) { + handerInvoker.rename_sentry_privilege(request, tResponse); + } + return tResponse; + } + + private static class Response<T> { + private TSentryResponseStatus status; + private T content; + + Response() { + } + + Response(TSentryResponseStatus status) { + this(status, null); + } + + Response(TSentryResponseStatus status, T content) { + this.status = status; + this.content = content; + } + } + private interface RequestHandler <T>{ + Response<T> handle() throws Exception ; + } + + private static void validateClientVersion(int protocolVersion) throws SentryThriftAPIMismatchException { + if (ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT != protocolVersion) { + String msg = "Sentry thrift API protocol version mismatch: Client thrift version " + + "is: " + protocolVersion + " , server thrift version " + + "is " + ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT; + throw new SentryThriftAPIMismatchException(msg); + } + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/9351d19d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/generic/thrift/SentryGenericPolicyProcessorFactory.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/generic/thrift/SentryGenericPolicyProcessorFactory.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/generic/thrift/SentryGenericPolicyProcessorFactory.java new file mode 100644 index 0000000..311b020 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/generic/thrift/SentryGenericPolicyProcessorFactory.java @@ -0,0 +1,44 @@ +/** + * 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.api.generic.thrift; + +import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.api.common.ApiConstants.SentryPolicyServiceConstants; +import org.apache.sentry.provider.db.service.persistent.SentryStore; +import org.apache.sentry.service.thrift.ProcessorFactory; +import org.apache.thrift.TMultiplexedProcessor; +import org.apache.thrift.TProcessor; + +public class SentryGenericPolicyProcessorFactory extends ProcessorFactory { + + public SentryGenericPolicyProcessorFactory(Configuration conf) { + super(conf); + } + + @Override + public boolean register(TMultiplexedProcessor multiplexedProcessor, + SentryStore _) throws Exception { + SentryGenericPolicyProcessor processHandler = new SentryGenericPolicyProcessor(conf); + TProcessor processor = new SentryGenericPolicyProcessorWrapper<SentryGenericPolicyService.Iface>( + processHandler); + multiplexedProcessor.registerProcessor( + SentryPolicyServiceConstants.SENTRY_GENERIC_SERVICE_NAME, processor); + return true; + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/9351d19d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/ConfServlet.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/ConfServlet.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/ConfServlet.java new file mode 100644 index 0000000..8625487 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/ConfServlet.java @@ -0,0 +1,71 @@ +package org.apache.sentry.api.service.thrift; + +/** + * 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.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.hadoop.conf.Configuration; + +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; + +/** + * Servlet to print out all sentry configuration. + */ +public class ConfServlet extends HttpServlet { + public static final String CONF_CONTEXT_ATTRIBUTE = "sentry.conf"; + public static final String FORMAT_JSON = "json"; + public static final String FORMAT_XML = "xml"; + public static final String FORMAT_PARAM = "format"; + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String format = request.getParameter(FORMAT_PARAM); + if (format == null) { + format = FORMAT_XML; + } + + if (FORMAT_XML.equals(format)) { + response.setContentType("text/xml; charset=utf-8"); + } else if (FORMAT_JSON.equals(format)) { + response.setContentType("application/json; charset=utf-8"); + } + + Configuration conf = (Configuration)getServletContext().getAttribute( + CONF_CONTEXT_ATTRIBUTE); + assert conf != null; + + Writer out = response.getWriter(); + if (FORMAT_JSON.equals(format)) { + Configuration.dumpConfiguration(conf, out); + } else if (FORMAT_XML.equals(format)) { + conf.writeXml(out); + } else { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Bad format: " + escapeHtml(format)); + } + out.close(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/9351d19d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/LogLevelServlet.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/LogLevelServlet.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/LogLevelServlet.java new file mode 100644 index 0000000..af81d6f --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/LogLevelServlet.java @@ -0,0 +1,122 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.api.service.thrift; + +import org.apache.log4j.Level; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; + +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; + +public class LogLevelServlet extends HttpServlet { + private static final String LF = "\n"; + private static final String BR = "<br />"; + private static final String B_BR = "<b>%s</b><br />"; + private static final String FORMS_HEAD = + "<h1>" + "Log Level" + "</h1>" + + LF + BR + "<hr /><h3>Results</h3>" + + LF + " Submitted Log Name: " + B_BR; + private static final String FORMS_CONTENT_GET = + LF + " Effective level: " + B_BR; + private static final String FORMS_CONTENT_SET = + LF + " Submitted Level: " + B_BR + + LF + " Setting Level to %s" + BR + + LF + " Effective level: " + B_BR; + private static final String FORMS_END = + LF + BR + "<hr /><h3>Get / Set</h3>" + + LF + "<form>Log: <input type='text' size='50' name='log' /> " + + "<input type='submit' value='Get Log Level' />" + "</form>" + + LF + "<form>Log: <input type='text' size='50' name='log' /> " + + "Level: <input type='text' name='level' /> " + + "<input type='submit' value='Set Log Level' />" + "</form>"; + private static final String FORMS_GET = FORMS_HEAD + FORMS_CONTENT_GET; + private static final String FORMS_SET = FORMS_HEAD + FORMS_CONTENT_SET; + + /** + * Return parameter on servlet request for the given name + * + * @param request: Servlet request + * @param name: Name of parameter in servlet request + * @return Parameter in servlet request for the given name, return null if can't find parameter. + */ + private String getParameter(ServletRequest request, String name) { + String s = request.getParameter(name); + if (s == null) { + return null; + } + s = s.trim(); + return s.length() == 0 ? null : s; + } + + /** + * Check the validity of the log level. + * @param level: The log level to be checked + * @return + * true: The log level is valid + * false: The log level is invalid + */ + private boolean isLogLevelValid(String level) { + return level.equals(Level.toLevel(level).toString()); + } + + /** + * Parse the class name and log level in the http servlet request. + * If the request contains only class name, return the log level in the response message. + * If the request contains both class name and level, set the log level to the requested level + * and return the setting result in the response message. + */ + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String logName = getParameter(request, "log"); + String level = getParameter(request, "level"); + response.setContentType("text/html;charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + PrintWriter out = response.getWriter(); + + if (logName != null) { + Logger logInstance = LogManager.getLogger(logName); + if (level == null) { + out.write(String.format(FORMS_GET, + escapeHtml(logName), + logInstance.getEffectiveLevel().toString())); + } else if (isLogLevelValid(level)) { + logInstance.setLevel(Level.toLevel(level)); + out.write(String.format(FORMS_SET, + escapeHtml(logName), + escapeHtml(level), + escapeHtml(level), + logInstance.getEffectiveLevel().toString())); + } else { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid log level: " + escapeHtml(level)); + return; + } + } + out.write(FORMS_END); + out.close(); + response.flushBuffer(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/9351d19d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/PubSubServlet.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/PubSubServlet.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/PubSubServlet.java new file mode 100644 index 0000000..8da35f1 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/PubSubServlet.java @@ -0,0 +1,128 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.api.service.thrift; + +import org.apache.sentry.core.common.utils.PubSub; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; + +/** + * This servlet facilitates sending {topic, message } tuples to Servlet components + * subscribed to specific topics. + * <p> + * It uses publish-subscribe mechanism implemented by PubSub class. + * The form generated by this servlet consists of the following elements: + * <p> + * a) Topic: pull-down menu of existing topics, i.e. the topics registered with + * PubSub by calling PubSub.subscribe() API. This prevents entering invalid topic. + * <p> + * b) Message: text field for entering a message + * <p> + * c) Submit: button to submit (topic, message) tuple + * <p> + * d) Status: text area printing status of the request or help information. + */ +public class PubSubServlet extends HttpServlet { + + private static final Logger LOGGER = LoggerFactory.getLogger(PubSubServlet.class); + + private static final String FORM_GET = + "<!DOCTYPE html>" + + "<html>" + + "<body>" + + "<form>" + + "<br><br><b>Topic:</b><br><br>" + + "<select name='topic'/>%s</select>" + + "<br><br><b>Message:</b><br><br>" + + "<input type='text' size='50' name='message'/>" + + "<br><br>" + + "<input type='submit' value='Submit'/>" + + "</form>" + + "<br><br><b>Status:</b><br><br>" + + "<textarea rows='4' cols='50'>%s</textarea>" + + "</body>" + + "</html>"; + + /** + * Return parameter on servlet request for the given name + * + * @param request: Servlet request + * @param name: Name of parameter in servlet request + * @return Parameter in servlet request for the given name, return null if can't find parameter. + */ + private static String getParameter(ServletRequest request, String name) { + String s = request.getParameter(name); + if (s == null) { + return null; + } + s = s.trim(); + return s.isEmpty() ? null : s; + } + + /** + * Parse the topic and message values and submit them via PubSub.submit() API. + * Reject request for unknown topic, i.e. topic no one is subscribed to. + */ + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String topic = getParameter(request, "topic"); + String message = getParameter(request, "message"); + response.setContentType("text/html;charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + PrintWriter out = response.getWriter(); + + String msg = "Topic is required, Message is optional.\nValid topics: " + PubSub.getInstance().getTopics(); + if (topic != null) { + LOGGER.info("Submitting topic " + topic + ", message " + message); + try { + PubSub.getInstance().publish(PubSub.Topic.fromString(topic), message); + msg = "Submitted topic " + topic + ", message " + message; + } catch (Exception e) { + msg = "Failed to submit topic " + topic + ", message " + message + " - " + e.getMessage(); + LOGGER.error(msg); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); + return; + } + } + + StringBuilder topics = new StringBuilder(); + for (PubSub.Topic t : PubSub.getInstance().getTopics()) { + topics.append("<option>").append(t.getName()).append("</option>"); + } + + String output = String.format(FORM_GET, topics.toString(), escapeHtml(msg)); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("HTML Page: " + output); + } + out.write(output); + out.close(); + response.flushBuffer(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/9351d19d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryAdminServlet.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryAdminServlet.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryAdminServlet.java new file mode 100644 index 0000000..5dc6cd6 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryAdminServlet.java @@ -0,0 +1,132 @@ +/** + * 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.api.service.thrift; + +import com.google.gson.Gson; +import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.provider.db.service.persistent.SentryStore; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Admin Servlet is only used when SENTRY_WEB_ADMIN_SERVLET_ENABLED is true. + */ +public class SentryAdminServlet extends HttpServlet { + private static final String SHOW_ALL = "/showAll"; + // Here we use the same way as in com.codahale.metrics.servlets.AdminServlet, and just + // use the TEMPLATE as a static html with some links referenced to other debug pages. + private static final String TEMPLATE = "<!DOCTYPE HTML>\n"+ + "<html lang=\"en\">\n"+ + "<head>\n"+ + " <meta charset=\"utf-8\">\n"+ + " <title>Sentry Service Admin</title>\n"+ + " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"+ + " <meta name=\"description\" content=\"\">\n"+ + " <link href=\"css/bootstrap.min.css\" rel=\"stylesheet\">\n"+ + " <link href=\"css/bootstrap-theme.min.css\" rel=\"stylesheet\">\n"+ + " <link href=\"css/sentry.css\" rel=\"stylesheet\">\n"+ + "</head>\n"+ + "<body>\n"+ + "<nav class=\"navbar navbar-default navbar-fixed-top\">\n"+ + " <div class=\"container\">\n"+ + " <div class=\"navbar-header\">\n"+ + " <a class=\"navbar-brand\" href=\"#\"><img src=\"sentry.png\" alt=\"Sentry Logo\"/></a>\n"+ + " </div>\n"+ + " <div class=\"collapse navbar-collapse\">\n"+ + " <ul class=\"nav navbar-nav\">\n"+ + " <li class=\"active\"><a href=\"#\">Admin</a></li>\n"+ + " <li><a href=\"/metrics?pretty=true\">Metrics</a></li>\n"+ + " <li><a href=\"/threads\">Threads</a></li>\n"+ + " <li><a href=\"/conf\">Configuration</a></li>\n"+ + " <li><a href=\"/admin/showAll\">ShowAllRoles</a></li>\n"+ + " </ul>\n"+ + " </div>\n"+ + " </div>\n"+ + "</nav>\n"+ + "<div class=\"container\">\n"+ + " <ul>\n"+ + " <li><a href=\"/metrics?pretty=true\">Metrics</a></li>\n"+ + " <li><a href=\"/threads\">Threads</a></li>\n"+ + " <li><a href=\"/conf\">Configuration</a></li>\n"+ + " <li><a href=\"/admin/showAll\">ShowAllRoles</a></li>\n"+ + " </ul>\n"+ + "</div>\n"+ + "</body>\n"+ + "</html>"; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String uri = request.getPathInfo(); + if(uri != null && !uri.equals("/")) { + if (uri.equals(SHOW_ALL)) { + showAll(response); + } else { + response.sendError(404); + } + } else { + response.setStatus(200); + response.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + response.setContentType("text/html"); + PrintWriter writer = response.getWriter(); + try { + writer.println(TEMPLATE); + } finally { + writer.close(); + } + } + } + + /** + * Print out all the roles and privileges information as json format. + */ + private void showAll(HttpServletResponse response) + throws ServletException, IOException { + Configuration conf = (Configuration)getServletContext().getAttribute( + ConfServlet.CONF_CONTEXT_ATTRIBUTE); + assert conf != null; + + Writer out = response.getWriter(); + try { + SentryStore sentrystore = new SentryStore(conf); + Map<String, Set<TSentryPrivilege>> roleMap = new HashMap<>(); + Set<String> roleSet = sentrystore.getAllRoleNames(); + for (String roleName: roleSet) { + roleMap.put(roleName, sentrystore.getAllTSentryPrivilegesByRoleName(roleName)); + } + String json = new Gson().toJson(roleMap); + response.setContentType("application/json"); + response.setCharacterEncoding("UTF-8"); + out.write(json); + } catch (Exception e) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); + } + out.close(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/9351d19d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryAuthFilter.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryAuthFilter.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryAuthFilter.java new file mode 100644 index 0000000..23121ec --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryAuthFilter.java @@ -0,0 +1,89 @@ +/** + * 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.api.service.thrift; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Properties; +import java.util.Set; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.hadoop.security.authentication.server.AuthenticationFilter; +import org.apache.hadoop.util.StringUtils; +import org.apache.sentry.service.common.ServiceConstants.ServerConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Sets; + +/** + * SentryAuthFilter is a subclass of AuthenticationFilter, + * add authorization: Only allowed users could connect the web server. + */ +public class SentryAuthFilter extends AuthenticationFilter { + + private static final Logger LOG = LoggerFactory.getLogger(SentryAuthFilter.class); + + public static final String ALLOW_WEB_CONNECT_USERS = ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS; + + private Set<String> allowUsers; + + @Override + protected void doFilter(FilterChain filterChain, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + String userName = request.getRemoteUser(); + LOG.debug("Authenticating user: " + userName + " from request."); + if (!allowUsers.contains(userName)) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, + "Unauthorized user status code: " + HttpServletResponse.SC_FORBIDDEN); + throw new ServletException(userName + " is unauthorized. status code: " + HttpServletResponse.SC_FORBIDDEN); + } + super.doFilter(filterChain, request, response); + } + + /** + * Override <code>getConfiguration<code> to get <code>ALLOW_WEB_CONNECT_USERS<code>. + */ + @Override + protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException { + Properties props = new Properties(); + Enumeration<?> names = filterConfig.getInitParameterNames(); + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + if (name.startsWith(configPrefix)) { + String value = filterConfig.getInitParameter(name); + if (ALLOW_WEB_CONNECT_USERS.equals(name)) { + allowUsers = parseConnectUsersFromConf(value); + } else { + props.put(name.substring(configPrefix.length()), value); + } + } + } + return props; + } + + private static Set<String> parseConnectUsersFromConf(String value) { + //Removed the logic to convert the allowed users to lower case, as user names need to be case sensitive + return Sets.newHashSet(StringUtils.getStrings(value)); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/9351d19d/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryHealthCheckServletContextListener.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryHealthCheckServletContextListener.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryHealthCheckServletContextListener.java new file mode 100644 index 0000000..eb11c19 --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/api/service/thrift/SentryHealthCheckServletContextListener.java @@ -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. + */ +package org.apache.sentry.api.service.thrift; + +import com.codahale.metrics.health.HealthCheckRegistry; +import com.codahale.metrics.servlets.HealthCheckServlet; + +/** + * Use this class's registry to register health checks: Can be some tests which make sure Sentry service is healthy + */ +public class SentryHealthCheckServletContextListener extends HealthCheckServlet.ContextListener { + + //This is just a place holder for health check registry, with out this AdminServlet throws out an error + public static final HealthCheckRegistry HEALTH_CHECK_REGISTRY = new HealthCheckRegistry(); + + @Override + protected HealthCheckRegistry getHealthCheckRegistry() { + return HEALTH_CHECK_REGISTRY; + } +} \ No newline at end of file
