| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.sentry.provider.db.service.thrift; |
| |
| import java.io.IOException; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| import java.util.concurrent.atomic.AtomicLong; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| |
| import com.codahale.metrics.Timer; |
| |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.hive.conf.HiveConf; |
| import org.apache.hadoop.hive.metastore.HiveMetaStoreClient; |
| import org.apache.hadoop.hive.metastore.api.Database; |
| import org.apache.hadoop.hive.metastore.api.Partition; |
| import org.apache.hadoop.hive.metastore.api.Table; |
| import org.apache.sentry.SentryUserException; |
| import org.apache.sentry.core.model.db.AccessConstants; |
| import org.apache.sentry.provider.common.GroupMappingService; |
| import org.apache.sentry.provider.db.SentryAccessDeniedException; |
| import org.apache.sentry.provider.db.SentryAlreadyExistsException; |
| import org.apache.sentry.provider.db.SentryInvalidInputException; |
| import org.apache.sentry.provider.db.SentryNoSuchObjectException; |
| import org.apache.sentry.provider.db.SentryPolicyStorePlugin; |
| import org.apache.sentry.provider.db.SentryPolicyStorePlugin.SentryPluginException; |
| import org.apache.sentry.provider.db.log.entity.JsonLogEntity; |
| 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.persistent.CommitContext; |
| import org.apache.sentry.provider.db.service.persistent.HAContext; |
| import org.apache.sentry.provider.db.service.persistent.ServiceRegister; |
| import org.apache.sentry.provider.db.service.persistent.DbSentryStore; |
| import org.apache.sentry.provider.db.service.persistent.SentryStore; |
| import org.apache.sentry.provider.db.service.persistent.SentryStoreFactory; |
| import org.apache.sentry.provider.db.service.thrift.PolicyStoreConstants.PolicyStoreServerConfig; |
| import org.apache.sentry.service.thrift.ServiceConstants.ConfUtilties; |
| import org.apache.sentry.service.thrift.ServiceConstants.ClientConfig; |
| import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig; |
| import org.apache.sentry.service.thrift.ProcessorFactory; |
| import org.apache.sentry.service.thrift.ServiceConstants.ThriftConstants; |
| import org.apache.sentry.service.thrift.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.Preconditions; |
| import com.google.common.base.Splitter; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Sets; |
| |
| @SuppressWarnings("unused") |
| public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface { |
| private static final Logger LOGGER = LoggerFactory.getLogger(SentryPolicyStoreProcessor.class); |
| private static final Logger AUDIT_LOGGER = LoggerFactory.getLogger(Constants.AUDIT_LOGGER_NAME); |
| |
| public static final String SENTRY_POLICY_SERVICE_NAME = "SentryPolicyService"; |
| |
| public static volatile SentryPolicyStoreProcessor instance; |
| |
| private final String name; |
| private final Configuration conf; |
| private final SentryStore sentryStore; |
| private final NotificationHandlerInvoker notificationHandlerInvoker; |
| private final ImmutableSet<String> adminGroups; |
| private boolean isReady; |
| SentryMetrics sentryMetrics; |
| private HAContext haContext; |
| |
| private List<SentryPolicyStorePlugin> sentryPlugins = new LinkedList<SentryPolicyStorePlugin>(); |
| |
| public SentryPolicyStoreProcessor(String name, Configuration conf) throws Exception { |
| super(); |
| this.name = name; |
| this.conf = conf; |
| this.notificationHandlerInvoker = new NotificationHandlerInvoker(conf, |
| createHandlers(conf)); |
| isReady = false; |
| if(conf.getBoolean(ServerConfig.SENTRY_HA_ENABLED, |
| ServerConfig.SENTRY_HA_ENABLED_DEFAULT)){ |
| haContext = HAContext.get(conf); |
| sentryStore = SentryStoreFactory.createSentryStore(conf); |
| ServiceRegister reg = new ServiceRegister(haContext); |
| reg.regService(conf.get(ServerConfig.RPC_ADDRESS), |
| conf.getInt(ServerConfig.RPC_PORT,ServerConfig.RPC_PORT_DEFAULT)); |
| } else { |
| sentryStore = SentryStoreFactory.createSentryStore(conf); |
| } |
| isReady = true; |
| adminGroups = ImmutableSet.copyOf(toTrimedLower(Sets.newHashSet(conf.getStrings( |
| ServerConfig.ADMIN_GROUPS, new String[]{})))); |
| Iterable<String> pluginClasses = ConfUtilties.CLASS_SPLITTER |
| .split(conf.get(ServerConfig.SENTRY_POLICY_STORE_PLUGINS, |
| ServerConfig.SENTRY_POLICY_STORE_PLUGINS_DEFAULT).trim()); |
| for (String pluginClassStr : pluginClasses) { |
| Class<?> clazz = conf.getClassByName(pluginClassStr); |
| if (!SentryPolicyStorePlugin.class.isAssignableFrom(clazz)) { |
| throw new IllegalArgumentException("Sentry Plugin [" |
| + pluginClassStr + "] is not a " |
| + SentryPolicyStorePlugin.class.getName()); |
| } |
| SentryPolicyStorePlugin plugin = (SentryPolicyStorePlugin)clazz.newInstance(); |
| plugin.initialize(conf, sentryStore); |
| sentryPlugins.add(plugin); |
| } |
| if (instance == null) { |
| instance = this; |
| } |
| initMetrics(); |
| } |
| |
| private void initMetrics() { |
| sentryMetrics = SentryMetrics.getInstance(); |
| sentryMetrics.addSentryStoreGauges(sentryStore); |
| |
| String sentryReporting = conf.get(ServerConfig.SENTRY_REPORTER); |
| if( sentryReporting != null) { |
| SentryMetrics.Reporting reporting; |
| try { |
| reporting = SentryMetrics.Reporting.valueOf(sentryReporting.toUpperCase()); |
| sentryMetrics.initReporting(reporting); |
| |
| } catch (IllegalArgumentException e) { |
| LOGGER.warn("Metrics reporting not configured correctly, please set " + ServerConfig.SENTRY_REPORTER + |
| " to: " + ServerConfig.SENTRY_REPORTER_CONSOLE + "/" + ServerConfig.SENTRY_REPORTER_JMX); |
| } |
| } |
| } |
| |
| public void stop() { |
| if (isReady) { |
| sentryStore.stop(); |
| } |
| if (haContext != null) { |
| try { |
| haContext.getCuratorFramework().close(); |
| } catch (Exception e) { |
| } |
| } |
| } |
| |
| public void registerPlugin(SentryPolicyStorePlugin plugin) throws SentryPluginException { |
| plugin.initialize(conf, sentryStore); |
| sentryPlugins.add(plugin); |
| } |
| |
| @VisibleForTesting |
| static List<NotificationHandler> createHandlers(Configuration conf) |
| throws SentryConfigurationException { |
| List<NotificationHandler> handlers = Lists.newArrayList(); |
| Iterable<String> notificationHandlers = Splitter.onPattern("[\\s,]").trimResults() |
| .omitEmptyStrings().split(conf.get(PolicyStoreServerConfig.NOTIFICATION_HANDLERS, "")); |
| for (String notificationHandler : notificationHandlers) { |
| Class<?> clazz = null; |
| try { |
| clazz = Class.forName(notificationHandler); |
| if (!NotificationHandler.class.isAssignableFrom(clazz)) { |
| throw new SentryConfigurationException("Class " + notificationHandler + " is not a " + |
| NotificationHandler.class.getName()); |
| } |
| } catch (ClassNotFoundException e) { |
| throw new SentryConfigurationException("Value " + notificationHandler + |
| " is not a class", e); |
| } |
| Preconditions.checkNotNull(clazz, "Error class cannot be null"); |
| try { |
| Constructor<?> constructor = clazz.getConstructor(Configuration.class); |
| handlers.add((NotificationHandler)constructor.newInstance(conf)); |
| } catch (Exception e) { |
| throw new SentryConfigurationException("Error attempting to create " + notificationHandler, e); |
| } |
| } |
| return handlers; |
| } |
| |
| @VisibleForTesting |
| public Configuration getSentryStoreConf() { |
| return conf; |
| } |
| |
| private static Set<String> toTrimedLower(Set<String> s) { |
| Set<String> result = Sets.newHashSet(); |
| for (String v : s) { |
| result.add(v.trim().toLowerCase()); |
| } |
| return result; |
| } |
| |
| private boolean inAdminGroups(Set<String> requestorGroups) { |
| requestorGroups = toTrimedLower(requestorGroups); |
| if (Sets.intersection(adminGroups, requestorGroups).isEmpty()) { |
| return false; |
| } else return true; |
| } |
| 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 denied to " + requestorUser); |
| } |
| } |
| |
| @Override |
| public TCreateSentryRoleResponse create_sentry_role( |
| TCreateSentryRoleRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.createRoleTimer.time(); |
| TCreateSentryRoleResponse response = new TCreateSentryRoleResponse(); |
| try { |
| authorize(request.getRequestorUserName(), |
| getRequestorGroups(request.getRequestorUserName())); |
| CommitContext commitContext = sentryStore.createSentryRole(request.getRoleName()); |
| response.setStatus(Status.OK()); |
| notificationHandlerInvoker.create_sentry_role(commitContext, |
| request, response); |
| } catch (SentryAlreadyExistsException e) { |
| String msg = "Role: " + request + " already exists."; |
| LOGGER.error(msg, e); |
| response.setStatus(Status.AlreadyExists(msg, e)); |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(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)); |
| } finally { |
| timerContext.stop(); |
| } |
| |
| AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance().createJsonLogEntity( |
| request, response, conf).toJsonFormatLog()); |
| return response; |
| } |
| |
| @Override |
| public TAlterSentryRoleGrantPrivilegeResponse alter_sentry_role_grant_privilege |
| (TAlterSentryRoleGrantPrivilegeRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.grantTimer.time(); |
| |
| TAlterSentryRoleGrantPrivilegeResponse response = new TAlterSentryRoleGrantPrivilegeResponse(); |
| try { |
| // There should only one field be set |
| if ( !(request.isSetPrivileges()^request.isSetPrivilege()) ) { |
| throw new SentryUserException("SENTRY API version is not right!"); |
| } |
| // Maintain compatibility for old API: Set privilege field to privileges field |
| if (request.isSetPrivilege()) { |
| request.setPrivileges(Sets.newHashSet(request.getPrivilege())); |
| } |
| CommitContext commitContext = sentryStore.alterSentryRoleGrantPrivileges(request.getRequestorUserName(), |
| request.getRoleName(), request.getPrivileges()); |
| response.setStatus(Status.OK()); |
| response.setPrivileges(request.getPrivileges()); |
| // Maintain compatibility for old API: Set privilege field to response |
| if (response.isSetPrivileges() && response.getPrivileges().size() == 1) { |
| response.setPrivilege(response.getPrivileges().iterator().next()); |
| } |
| notificationHandlerInvoker.alter_sentry_role_grant_privilege(commitContext, |
| request, response); |
| for (SentryPolicyStorePlugin plugin : sentryPlugins) { |
| plugin.onAlterSentryRoleGrantPrivilege(request); |
| } |
| } catch (SentryNoSuchObjectException e) { |
| String msg = "Role: " + request.getRoleName() + " doesn't exist."; |
| LOGGER.error(msg, e); |
| response.setStatus(Status.NoSuchObject(msg, e)); |
| } catch (SentryInvalidInputException e) { |
| String msg = "Invalid input privilege object"; |
| LOGGER.error(msg, e); |
| response.setStatus(Status.InvalidInput(msg, e)); |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(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)); |
| } finally { |
| timerContext.stop(); |
| } |
| |
| Set<JsonLogEntity> jsonLogEntitys = JsonLogEntityFactory.getInstance().createJsonLogEntitys( |
| request, response, conf); |
| for (JsonLogEntity jsonLogEntity : jsonLogEntitys) { |
| AUDIT_LOGGER.info(jsonLogEntity.toJsonFormatLog()); |
| } |
| return response; |
| } |
| |
| @Override |
| public TAlterSentryRoleRevokePrivilegeResponse alter_sentry_role_revoke_privilege |
| (TAlterSentryRoleRevokePrivilegeRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.revokeTimer.time(); |
| TAlterSentryRoleRevokePrivilegeResponse response = new TAlterSentryRoleRevokePrivilegeResponse(); |
| try { |
| // There should only one field be set |
| if ( !(request.isSetPrivileges()^request.isSetPrivilege()) ) { |
| throw new SentryUserException("SENTRY API version is not right!"); |
| } |
| // Maintain compatibility for old API: Set privilege field to privileges field |
| if (request.isSetPrivilege()) { |
| request.setPrivileges(Sets.newHashSet(request.getPrivilege())); |
| } |
| CommitContext commitContext = sentryStore.alterSentryRoleRevokePrivileges(request.getRequestorUserName(), |
| request.getRoleName(), request.getPrivileges()); |
| response.setStatus(Status.OK()); |
| notificationHandlerInvoker.alter_sentry_role_revoke_privilege(commitContext, |
| request, response); |
| for (SentryPolicyStorePlugin plugin : sentryPlugins) { |
| plugin.onAlterSentryRoleRevokePrivilege(request); |
| } |
| } catch (SentryNoSuchObjectException e) { |
| StringBuilder msg = new StringBuilder(); |
| if (request.getPrivileges().size() > 0) { |
| for (TSentryPrivilege privilege : request.getPrivileges()) { |
| msg.append("Privilege: [server="); |
| msg.append(privilege.getServerName()); |
| msg.append(",db="); |
| msg.append(privilege.getDbName()); |
| msg.append(",table="); |
| msg.append(privilege.getTableName()); |
| msg.append(",URI="); |
| msg.append(privilege.getURI()); |
| msg.append(",action="); |
| msg.append(privilege.getAction()); |
| msg.append("] "); |
| } |
| msg.append("doesn't exist."); |
| } |
| LOGGER.error(msg.toString(), e); |
| response.setStatus(Status.NoSuchObject(msg.toString(), e)); |
| } catch (SentryInvalidInputException e) { |
| String msg = "Invalid input privilege object"; |
| LOGGER.error(msg, e); |
| response.setStatus(Status.InvalidInput(msg, e)); |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(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)); |
| } finally { |
| timerContext.stop(); |
| } |
| |
| Set<JsonLogEntity> jsonLogEntitys = JsonLogEntityFactory.getInstance().createJsonLogEntitys( |
| request, response, conf); |
| for (JsonLogEntity jsonLogEntity : jsonLogEntitys) { |
| AUDIT_LOGGER.info(jsonLogEntity.toJsonFormatLog()); |
| } |
| return response; |
| } |
| |
| @Override |
| public TDropSentryRoleResponse drop_sentry_role( |
| TDropSentryRoleRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.dropRoleTimer.time(); |
| TDropSentryRoleResponse response = new TDropSentryRoleResponse(); |
| TSentryResponseStatus status; |
| try { |
| authorize(request.getRequestorUserName(), |
| getRequestorGroups(request.getRequestorUserName())); |
| CommitContext commitContext = sentryStore.dropSentryRole(request.getRoleName()); |
| response.setStatus(Status.OK()); |
| notificationHandlerInvoker.drop_sentry_role(commitContext, |
| request, response); |
| for (SentryPolicyStorePlugin plugin : sentryPlugins) { |
| plugin.onDropSentryRole(request); |
| } |
| } catch (SentryNoSuchObjectException e) { |
| String msg = "Role :" + request + " does not exist."; |
| LOGGER.error(msg, e); |
| response.setStatus(Status.NoSuchObject(msg, e)); |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(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)); |
| } finally { |
| timerContext.stop(); |
| } |
| |
| AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance().createJsonLogEntity( |
| request, response, conf).toJsonFormatLog()); |
| return response; |
| } |
| |
| @Override |
| public TAlterSentryRoleAddGroupsResponse alter_sentry_role_add_groups( |
| TAlterSentryRoleAddGroupsRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.grantRoleTimer.time(); |
| TAlterSentryRoleAddGroupsResponse response = new TAlterSentryRoleAddGroupsResponse(); |
| try { |
| authorize(request.getRequestorUserName(), |
| getRequestorGroups(request.getRequestorUserName())); |
| CommitContext commitContext = sentryStore.alterSentryRoleAddGroups(request.getRequestorUserName(), |
| request.getRoleName(), request.getGroups()); |
| response.setStatus(Status.OK()); |
| notificationHandlerInvoker.alter_sentry_role_add_groups(commitContext, |
| request, response); |
| for (SentryPolicyStorePlugin plugin : sentryPlugins) { |
| plugin.onAlterSentryRoleAddGroups(request); |
| } |
| } catch (SentryNoSuchObjectException e) { |
| String msg = "Role: " + request + " does not exist."; |
| LOGGER.error(msg, e); |
| response.setStatus(Status.NoSuchObject(msg, e)); |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(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)); |
| } finally { |
| timerContext.stop(); |
| } |
| |
| AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance().createJsonLogEntity( |
| request, response, conf).toJsonFormatLog()); |
| return response; |
| } |
| |
| @Override |
| public TAlterSentryRoleDeleteGroupsResponse alter_sentry_role_delete_groups( |
| TAlterSentryRoleDeleteGroupsRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.revokeRoleTimer.time(); |
| TAlterSentryRoleDeleteGroupsResponse response = new TAlterSentryRoleDeleteGroupsResponse(); |
| try { |
| authorize(request.getRequestorUserName(), |
| getRequestorGroups(request.getRequestorUserName())); |
| CommitContext commitContext = sentryStore.alterSentryRoleDeleteGroups(request.getRoleName(), |
| request.getGroups()); |
| response.setStatus(Status.OK()); |
| notificationHandlerInvoker.alter_sentry_role_delete_groups(commitContext, |
| request, response); |
| for (SentryPolicyStorePlugin plugin : sentryPlugins) { |
| plugin.onAlterSentryRoleDeleteGroups(request); |
| } |
| } catch (SentryNoSuchObjectException e) { |
| String msg = "Role: " + request + " does not exist."; |
| LOGGER.error(msg, e); |
| response.setStatus(Status.NoSuchObject(msg, e)); |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(e.getMessage(), e)); |
| } catch (Exception e) { |
| String msg = "Unknown error adding groups to role: " + request; |
| LOGGER.error(msg, e); |
| response.setStatus(Status.RuntimeError(msg, e)); |
| } finally { |
| timerContext.stop(); |
| } |
| |
| AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance().createJsonLogEntity( |
| request, response, conf).toJsonFormatLog()); |
| return response; |
| } |
| |
| @Override |
| public TListSentryRolesResponse list_sentry_roles_by_group( |
| TListSentryRolesRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.listRolesByGroupTimer.time(); |
| TListSentryRolesResponse response = new TListSentryRolesResponse(); |
| TSentryResponseStatus status; |
| Set<TSentryRole> roleSet = new HashSet<TSentryRole>(); |
| String subject = request.getRequestorUserName(); |
| boolean checkAllGroups = false; |
| try { |
| Set<String> groups = getRequestorGroups(subject); |
| // Don't check admin permissions for listing requestor's own roles |
| if (AccessConstants.ALL.equalsIgnoreCase(request.getGroupName())) { |
| checkAllGroups = true; |
| } else { |
| 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 denied to " + subject); |
| }else { |
| groups.clear(); |
| groups.add(request.getGroupName()); |
| } |
| } |
| roleSet = sentryStore.getTSentryRolesByGroupName(groups, checkAllGroups); |
| response.setRoles(roleSet); |
| response.setStatus(Status.OK()); |
| } catch (SentryNoSuchObjectException e) { |
| response.setRoles(roleSet); |
| String msg = "Role: " + request + " couldn't be retrieved."; |
| LOGGER.error(msg, e); |
| response.setStatus(Status.NoSuchObject(msg, e)); |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(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)); |
| } finally { |
| timerContext.stop(); |
| } |
| return response; |
| } |
| |
| @Override |
| public TListSentryPrivilegesResponse list_sentry_privileges_by_role( |
| TListSentryPrivilegesRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.listPrivilegesByRoleTimer.time(); |
| TListSentryPrivilegesResponse response = new TListSentryPrivilegesResponse(); |
| TSentryResponseStatus status; |
| Set<TSentryPrivilege> privilegeSet = new HashSet<TSentryPrivilege>(); |
| String subject = request.getRequestorUserName(); |
| try { |
| Set<String> groups = getRequestorGroups(subject); |
| Boolean admin = inAdminGroups(groups); |
| if(!admin) { |
| Set<String> roleNamesForGroups = toTrimedLower(sentryStore.getRoleNamesForGroups(groups)); |
| if(!roleNamesForGroups.contains(request.getRoleName().trim().toLowerCase())) { |
| throw new SentryAccessDeniedException("Access denied to " + subject); |
| } |
| } |
| if (request.isSetAuthorizableHierarchy()) { |
| TSentryAuthorizable authorizableHierarchy = request.getAuthorizableHierarchy(); |
| privilegeSet = sentryStore.getTSentryPrivileges(Sets.newHashSet(request.getRoleName()), authorizableHierarchy); |
| } else { |
| privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(request.getRoleName()); |
| } |
| response.setPrivileges(privilegeSet); |
| response.setStatus(Status.OK()); |
| } catch (SentryNoSuchObjectException e) { |
| response.setPrivileges(privilegeSet); |
| String msg = "Privilege: " + request + " couldn't be retrieved."; |
| LOGGER.error(msg, e); |
| response.setStatus(Status.NoSuchObject(msg, e)); |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(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)); |
| } finally { |
| timerContext.stop(); |
| } |
| return response; |
| } |
| |
| /** |
| * This method was created specifically for ProviderBackend.getPrivileges() and is not meant |
| * to be used for general privilege retrieval. More details in the .thrift file. |
| */ |
| @Override |
| public TListSentryPrivilegesForProviderResponse list_sentry_privileges_for_provider( |
| TListSentryPrivilegesForProviderRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.listPrivilegesForProviderTimer.time(); |
| TListSentryPrivilegesForProviderResponse response = new TListSentryPrivilegesForProviderResponse(); |
| response.setPrivileges(new HashSet<String>()); |
| try { |
| Set<String> privilegesForProvider = sentryStore.listSentryPrivilegesForProvider( |
| request.getGroups(), request.getRoleSet(), request.getAuthorizableHierarchy()); |
| response.setPrivileges(privilegesForProvider); |
| if (((privilegesForProvider == null)||(privilegesForProvider.size() == 0))&&(request.getAuthorizableHierarchy() != null)) { |
| if (sentryStore.hasAnyServerPrivileges( |
| request.getGroups(), request.getRoleSet(), request.getAuthorizableHierarchy().getServer())) { |
| |
| // REQUIRED for ensuring 'default' Db is accessible by any user |
| // with privileges to atleast 1 object with the specific server as root |
| |
| // Need some way to specify that even though user has no privilege |
| // For the specific AuthorizableHierarchy.. he has privilege on |
| // atleast 1 object in the server hierarchy |
| HashSet<String> serverPriv = Sets.newHashSet("server=+"); |
| response.setPrivileges(serverPriv); |
| } |
| } |
| response.setStatus(Status.OK()); |
| } catch (Exception e) { |
| String msg = "Unknown error for request: " + request + ", message: " + e.getMessage(); |
| LOGGER.error(msg, e); |
| response.setStatus(Status.RuntimeError(msg, e)); |
| } finally { |
| timerContext.stop(); |
| } |
| return response; |
| } |
| |
| // retrieve the group mapping for the given user name |
| private Set<String> getRequestorGroups(String userName) |
| throws SentryUserException { |
| return getGroupsFromUserName(this.conf, userName); |
| } |
| |
| public static Set<String> getGroupsFromUserName(Configuration conf, |
| String userName) throws SentryUserException { |
| String groupMapping = conf.get(ServerConfig.SENTRY_STORE_GROUP_MAPPING, |
| ServerConfig.SENTRY_STORE_GROUP_MAPPING_DEFAULT); |
| String authResoruce = conf |
| .get(ServerConfig.SENTRY_STORE_GROUP_MAPPING_RESOURCE); |
| |
| // load the group mapping provider class |
| GroupMappingService groupMappingService; |
| try { |
| Constructor<?> constrctor = Class.forName(groupMapping) |
| .getDeclaredConstructor(Configuration.class, String.class); |
| constrctor.setAccessible(true); |
| groupMappingService = (GroupMappingService) constrctor |
| .newInstance(new Object[] { conf, authResoruce }); |
| } catch (NoSuchMethodException e) { |
| throw new SentryUserException("Unable to instantiate group mapping", e); |
| } catch (SecurityException e) { |
| throw new SentryUserException("Unable to instantiate group mapping", e); |
| } catch (ClassNotFoundException e) { |
| throw new SentryUserException("Unable to instantiate group mapping", e); |
| } catch (InstantiationException e) { |
| throw new SentryUserException("Unable to instantiate group mapping", e); |
| } catch (IllegalAccessException e) { |
| throw new SentryUserException("Unable to instantiate group mapping", e); |
| } catch (IllegalArgumentException e) { |
| throw new SentryUserException("Unable to instantiate group mapping", e); |
| } catch (InvocationTargetException e) { |
| throw new SentryUserException("Unable to instantiate group mapping", e); |
| } |
| return groupMappingService.getGroups(userName); |
| } |
| |
| @Override |
| public TDropPrivilegesResponse drop_sentry_privilege( |
| TDropPrivilegesRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.dropPrivilegeTimer.time(); |
| TDropPrivilegesResponse response = new TDropPrivilegesResponse(); |
| try { |
| authorize(request.getRequestorUserName(), adminGroups); |
| sentryStore.dropPrivilege(request.getAuthorizable()); |
| for (SentryPolicyStorePlugin plugin : sentryPlugins) { |
| plugin.onDropSentryPrivilege(request); |
| } |
| response.setStatus(Status.OK()); |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(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)); |
| } finally { |
| timerContext.stop(); |
| } |
| return response; |
| } |
| |
| @Override |
| public TRenamePrivilegesResponse rename_sentry_privilege( |
| TRenamePrivilegesRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.renamePrivilegeTimer.time(); |
| TRenamePrivilegesResponse response = new TRenamePrivilegesResponse(); |
| try { |
| authorize(request.getRequestorUserName(), adminGroups); |
| sentryStore.renamePrivilege(request.getOldAuthorizable(), |
| request.getNewAuthorizable()); |
| for (SentryPolicyStorePlugin plugin : sentryPlugins) { |
| plugin.onRenameSentryPrivilege(request); |
| } |
| response.setStatus(Status.OK()); |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(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)); |
| } finally { |
| timerContext.close(); |
| } |
| return response; |
| } |
| |
| @Override |
| public TListSentryPrivilegesByAuthResponse list_sentry_privileges_by_authorizable( |
| TListSentryPrivilegesByAuthRequest request) throws TException { |
| final Timer.Context timerContext = sentryMetrics.listPrivilegesByAuthorizableTimer.time(); |
| TListSentryPrivilegesByAuthResponse response = new TListSentryPrivilegesByAuthResponse(); |
| Map<TSentryAuthorizable, TSentryPrivilegeMap> authRoleMap = Maps.newHashMap(); |
| String subject = request.getRequestorUserName(); |
| Set<String> requestedGroups = request.getGroups(); |
| TSentryActiveRoleSet requestedRoleSet = request.getRoleSet(); |
| try { |
| Set<String> memberGroups = getRequestorGroups(subject); |
| if(!inAdminGroups(memberGroups)) { |
| // disallow non-admin to lookup groups that they are not part of |
| if (requestedGroups != null && !requestedGroups.isEmpty()) { |
| for (String requestedGroup : requestedGroups) { |
| if (!memberGroups.contains(requestedGroup)) { |
| // if user doesn't belong to one of the requested group then raise error |
| throw new SentryAccessDeniedException("Access denied to " + subject); |
| } |
| } |
| } else { |
| // non-admin's search is limited to it's own groups |
| requestedGroups = memberGroups; |
| } |
| |
| // disallow non-admin to lookup roles that they are not part of |
| if (requestedRoleSet != null && !requestedRoleSet.isAll()) { |
| Set<String> roles = toTrimedLower(sentryStore |
| .getRoleNamesForGroups(memberGroups)); |
| for (String role : toTrimedLower(requestedRoleSet.getRoles())) { |
| if (!roles.contains(role)) { |
| throw new SentryAccessDeniedException("Access denied to " |
| + subject); |
| } |
| } |
| } |
| } |
| |
| // If user is not part of any group.. return empty response |
| for (TSentryAuthorizable authorizable : request.getAuthorizableSet()) { |
| authRoleMap.put(authorizable, sentryStore |
| .listSentryPrivilegesByAuthorizable(requestedGroups, |
| request.getRoleSet(), authorizable, inAdminGroups(memberGroups))); |
| } |
| response.setPrivilegesMapByAuth(authRoleMap); |
| response.setStatus(Status.OK()); |
| // TODO : Sentry - HDFS : Have to handle this |
| } catch (SentryAccessDeniedException e) { |
| LOGGER.error(e.getMessage(), e); |
| response.setStatus(Status.AccessDenied(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)); |
| } finally { |
| timerContext.stop(); |
| } |
| return response; |
| } |
| |
| /** |
| * Respond to a request for a config value in the sentry server. The client |
| * can request any config value that starts with "sentry." and doesn't contain |
| * "keytab". |
| * @param request Contains config parameter sought and default if not found |
| * @return The response, containing the value and status |
| * @throws TException |
| */ |
| @Override |
| public TSentryConfigValueResponse get_sentry_config_value( |
| TSentryConfigValueRequest request) throws TException { |
| |
| final String requirePattern = "^sentry\\..*"; |
| final String excludePattern = ".*keytab.*|.*\\.jdbc\\..*|.*password.*"; |
| |
| TSentryConfigValueResponse response = new TSentryConfigValueResponse(); |
| String attr = request.getPropertyName(); |
| |
| // Only allow config parameters like... |
| if (!Pattern.matches(requirePattern, attr) || |
| Pattern.matches(excludePattern, attr)) { |
| String msg = "Attempted access of the configuration property " + attr + |
| " was denied"; |
| LOGGER.error(msg); |
| response.setStatus(Status.AccessDenied(msg, |
| new SentryAccessDeniedException(msg))); |
| return response; |
| } |
| |
| response.setValue(conf.get(attr,request.getDefaultValue())); |
| response.setStatus(Status.OK()); |
| return response; |
| } |
| } |