blob: 1ffc496fc1eae1a987b7ea5892db1e3d04c10af4 [file] [log] [blame]
/**
* 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;
}
}