You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sentry.apache.org by co...@apache.org on 2016/06/24 06:00:46 UTC

[17/44] sentry git commit: SENTRY-1287: Create sentry-service-server module(Colin Ma, reviewed by Dapeng Sun)

http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
new file mode 100644
index 0000000..5dff12a
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
@@ -0,0 +1,1111 @@
+/**
+ * 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.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+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 org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.core.common.exception.SentryUserException;
+import org.apache.sentry.core.common.exception.SentrySiteConfigurationException;
+import org.apache.sentry.core.model.db.AccessConstants;
+import org.apache.sentry.core.common.service.GroupMappingService;
+import org.apache.sentry.core.common.utils.PolicyFileConstants;
+import org.apache.sentry.core.common.exception.SentryGroupNotFoundException;
+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.provider.db.SentryPolicyStorePlugin;
+import org.apache.sentry.provider.db.SentryPolicyStorePlugin.SentryPluginException;
+import org.apache.sentry.core.common.exception.SentryThriftAPIMismatchException;
+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.SentryStore;
+import org.apache.sentry.provider.db.service.persistent.ServiceRegister;
+import org.apache.sentry.provider.db.service.thrift.PolicyStoreConstants.PolicyStoreServerConfig;
+import org.apache.sentry.service.thrift.SentryServiceUtil;
+import org.apache.sentry.service.thrift.ServiceConstants;
+import org.apache.sentry.service.thrift.ServiceConstants.ConfUtilties;
+import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
+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.codahale.metrics.Timer;
+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 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.getHAServerContext(conf);
+      sentryStore = new SentryStore(conf);
+      ServiceRegister reg = new ServiceRegister(haContext);
+      reg.regService(conf.get(ServerConfig.RPC_ADDRESS),
+          conf.getInt(ServerConfig.RPC_PORT,ServerConfig.RPC_PORT_DEFAULT));
+    } else {
+      sentryStore = new SentryStore(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: " + SentryMetrics.Reporting.CONSOLE.name() + "/" + SentryMetrics.Reporting.JMX.name());
+      }
+    }
+  }
+
+  public void stop() {
+    if (isReady) {
+      sentryStore.stop();
+    }
+    if (haContext != null) {
+      try {
+        haContext.getCuratorFramework().close();
+      } catch (Exception e) {
+        LOGGER.warn("Error in stopping processor", e);
+      }
+    }
+  }
+
+  public void registerPlugin(SentryPolicyStorePlugin plugin) throws SentryPluginException {
+    plugin.initialize(conf, sentryStore);
+    sentryPlugins.add(plugin);
+  }
+
+  @VisibleForTesting
+  static List<NotificationHandler> createHandlers(Configuration conf)
+  throws SentrySiteConfigurationException {
+    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 SentrySiteConfigurationException("Class " + notificationHandler + " is not a " +
+                                                 NotificationHandler.class.getName());
+        }
+      } catch (ClassNotFoundException e) {
+        throw new SentrySiteConfigurationException("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 SentrySiteConfigurationException("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) {
+    Set<String> trimmedRequestorGroups = toTrimedLower(requestorGroups);
+    return !Sets.intersection(adminGroups, trimmedRequestorGroups).isEmpty();
+  }
+  
+  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 {
+      validateClientVersion(request.getProtocol_version());
+      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 (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));
+    } finally {
+      timerContext.stop();
+    }
+
+    try {
+      AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance()
+          .createJsonLogEntity(request, response, conf).toJsonFormatLog());
+    } catch (Exception e) {
+      // if any exception, log the exception.
+      String msg = "Error creating audit log for create role: " + e.getMessage();
+      LOGGER.error(msg, e);
+    }
+    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 {
+      validateClientVersion(request.getProtocol_version());
+      // 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 (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));
+    } finally {
+      timerContext.stop();
+    }
+
+    try {
+      Set<JsonLogEntity> jsonLogEntitys = JsonLogEntityFactory.getInstance().createJsonLogEntitys(
+          request, response, conf);
+      for (JsonLogEntity jsonLogEntity : jsonLogEntitys) {
+        AUDIT_LOGGER.info(jsonLogEntity.toJsonFormatLog());
+      }
+    } catch (Exception e) {
+      // if any exception, log the exception.
+      String msg = "Error creating audit log for grant privilege to role: " + e.getMessage();
+      LOGGER.error(msg, e);
+    }
+    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 {
+      validateClientVersion(request.getProtocol_version());
+      // 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 (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));
+    } finally {
+      timerContext.stop();
+    }
+
+    try {
+      Set<JsonLogEntity> jsonLogEntitys = JsonLogEntityFactory.getInstance().createJsonLogEntitys(
+          request, response, conf);
+      for (JsonLogEntity jsonLogEntity : jsonLogEntitys) {
+        AUDIT_LOGGER.info(jsonLogEntity.toJsonFormatLog());
+      }
+    } catch (Exception e) {
+      // if any exception, log the exception.
+      String msg = "Error creating audit log for revoke privilege from role: " + e.getMessage();
+      LOGGER.error(msg, e);
+    }
+    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 {
+      validateClientVersion(request.getProtocol_version());
+      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 + " doesn't 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 (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));
+    } finally {
+      timerContext.stop();
+    }
+
+    try {
+      AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance()
+          .createJsonLogEntity(request, response, conf).toJsonFormatLog());
+    } catch (Exception e) {
+      // if any exception, log the exception.
+      String msg = "Error creating audit log for drop role: " + e.getMessage();
+      LOGGER.error(msg, e);
+    }
+    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 {
+      validateClientVersion(request.getProtocol_version());
+      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 + " doesn't 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 (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));
+    } finally {
+      timerContext.stop();
+    }
+
+    try {
+      AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance()
+          .createJsonLogEntity(request, response, conf).toJsonFormatLog());
+    } catch (Exception e) {
+      // if any exception, log the exception.
+      String msg = "Error creating audit log for add role to group: " + e.getMessage();
+      LOGGER.error(msg, e);
+    }
+    return response;
+  }
+
+  @Override
+  public TAlterSentryRoleAddUsersResponse alter_sentry_role_add_users(
+      TAlterSentryRoleAddUsersRequest request) throws TException {
+    final Timer.Context timerContext = sentryMetrics.grantRoleTimer.time();
+    TAlterSentryRoleAddUsersResponse response = new TAlterSentryRoleAddUsersResponse();
+    try {
+      validateClientVersion(request.getProtocol_version());
+      authorize(request.getRequestorUserName(), getRequestorGroups(request.getRequestorUserName()));
+      CommitContext commitContext = sentryStore.alterSentryRoleAddUsers(request.getRoleName(),
+          request.getUsers());
+      response.setStatus(Status.OK());
+      notificationHandlerInvoker.alter_sentry_role_add_users(commitContext, request, response);
+    } 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 (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));
+    } finally {
+      timerContext.stop();
+    }
+
+    try {
+      AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance()
+          .createJsonLogEntity(request, response, conf).toJsonFormatLog());
+    } catch (Exception e) {
+      // if any exception, log the exception.
+      String msg = "Error creating audit log for add role to user: " + e.getMessage();
+      LOGGER.error(msg, e);
+    }
+    return response;
+  }
+
+  @Override
+  public TAlterSentryRoleDeleteUsersResponse alter_sentry_role_delete_users(
+      TAlterSentryRoleDeleteUsersRequest request) throws TException {
+    final Timer.Context timerContext = sentryMetrics.grantRoleTimer.time();
+    TAlterSentryRoleDeleteUsersResponse response = new TAlterSentryRoleDeleteUsersResponse();
+    try {
+      validateClientVersion(request.getProtocol_version());
+      authorize(request.getRequestorUserName(), getRequestorGroups(request.getRequestorUserName()));
+      CommitContext commitContext = sentryStore.alterSentryRoleDeleteUsers(request.getRoleName(),
+          request.getUsers());
+      response.setStatus(Status.OK());
+      notificationHandlerInvoker.alter_sentry_role_delete_users(commitContext, request, response);
+    } 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 (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));
+    } finally {
+      timerContext.stop();
+    }
+
+    try {
+      AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance()
+          .createJsonLogEntity(request, response, conf).toJsonFormatLog());
+   } catch (Exception e) {
+      // if any exception, log the exception.
+      String msg = "Error creating audit log for delete role from user: " + e.getMessage();
+      LOGGER.error(msg, e);
+    }
+    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 {
+      validateClientVersion(request.getProtocol_version());
+      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 (SentryThriftAPIMismatchException e) {
+      LOGGER.error(e.getMessage(), e);
+      response.setStatus(Status.THRIFT_VERSION_MISMATCH(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();
+    }
+
+    try {
+      AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance()
+          .createJsonLogEntity(request, response, conf).toJsonFormatLog());
+    } catch (Exception e) {
+      // if any exception, log the exception.
+      String msg = "Error creating audit log for delete role from group: " + e.getMessage();
+      LOGGER.error(msg, e);
+    }
+    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 {
+      validateClientVersion(request.getProtocol_version());
+      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 = "Request: " + request + " couldn't be completed, message: " + e.getMessage();
+      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 (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));
+    } finally {
+      timerContext.stop();
+    }
+    return response;
+  }
+
+  public TListSentryRolesResponse list_sentry_roles_by_user(TListSentryRolesForUserRequest request)
+      throws TException {
+    final Timer.Context timerContext = sentryMetrics.listRolesByGroupTimer.time();
+    TListSentryRolesResponse response = new TListSentryRolesResponse();
+    TSentryResponseStatus status;
+    Set<TSentryRole> roleSet = new HashSet<TSentryRole>();
+    String requestor = request.getRequestorUserName();
+    String userName = request.getUserName();
+    boolean checkAllGroups = false;
+    try {
+      validateClientVersion(request.getProtocol_version());
+      // userName can't be empty
+      if (StringUtils.isEmpty(userName)) {
+        throw new SentryAccessDeniedException("The user name can't be empty.");
+      }
+
+      Set<String> requestorGroups = getRequestorGroups(requestor);
+      Set<String> userGroups = getRequestorGroups(userName);
+      boolean isAdmin = inAdminGroups(requestorGroups);
+
+      // Only admin users can list other user's roles in the system
+      // Non admin users are only allowed to list only their own roles related user and group
+      if (!isAdmin && !userName.equals(requestor)) {
+        throw new SentryAccessDeniedException("Access denied to list the roles for " + userName);
+      }
+      roleSet = sentryStore.getTSentryRolesByUserNames(Sets.newHashSet(userName));
+      response.setRoles(roleSet);
+      response.setStatus(Status.OK());
+    } catch (SentryGroupNotFoundException e) {
+      LOGGER.error(e.getMessage(), e);
+      String msg = "Group couldn't be retrieved for " + requestor + " or " + userName + ".";
+      response.setStatus(Status.AccessDenied(msg, e));
+    } 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 (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));
+    } 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 {
+      validateClientVersion(request.getProtocol_version());
+      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 (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));
+    } 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 {
+      validateClientVersion(request.getProtocol_version());
+      Set<String> privilegesForProvider =
+          sentryStore.listSentryPrivilegesForProvider(request.getGroups(), request.getUsers(),
+              request.getRoleSet(), request.getAuthorizableHierarchy());
+      response.setPrivileges(privilegesForProvider);
+      if (privilegesForProvider == null
+          || privilegesForProvider.size() == 0
+          && request.getAuthorizableHierarchy() != null
+          && sentryStore.hasAnyServerPrivileges(request.getGroups(), request.getUsers(),
+              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 (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));
+    } 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 {
+      validateClientVersion(request.getProtocol_version());
+      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 (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));
+    } 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 {
+      validateClientVersion(request.getProtocol_version());
+      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 (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));
+    } 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 {
+      validateClientVersion(request.getProtocol_version());
+      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 (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));
+    } 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();
+
+    try {
+      validateClientVersion(request.getProtocol_version());
+    } catch (SentryThriftAPIMismatchException e) {
+      LOGGER.error(e.getMessage(), e);
+      response.setStatus(Status.THRIFT_VERSION_MISMATCH(e.getMessage(), e));
+    }
+    // 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;
+  }
+
+  @VisibleForTesting
+  static void validateClientVersion(int protocolVersion) throws SentryThriftAPIMismatchException {
+    if (ServiceConstants.ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT != protocolVersion) {
+      String msg = "Sentry thrift API protocol version mismatch: Client thrift version " +
+          "is: " + protocolVersion + " , server thrift verion " +
+              "is " + ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT;
+      throw new SentryThriftAPIMismatchException(msg);
+    }
+  }
+
+  // get the sentry mapping data and return the data with map structure
+  @Override
+  public TSentryExportMappingDataResponse export_sentry_mapping_data(
+      TSentryExportMappingDataRequest request) throws TException {
+    TSentryExportMappingDataResponse response = new TSentryExportMappingDataResponse();
+    try {
+      String requestor = request.getRequestorUserName();
+      Set<String> memberGroups = getRequestorGroups(requestor);
+      String objectPath = request.getObjectPath();
+      String databaseName = null;
+      String tableName = null;
+
+      Map<String, String> objectMap =
+          SentryServiceUtil.parseObjectPath(objectPath);
+      databaseName = objectMap.get(PolicyFileConstants.PRIVILEGE_DATABASE_NAME);
+      tableName = objectMap.get(PolicyFileConstants.PRIVILEGE_TABLE_NAME);
+
+      if (!inAdminGroups(memberGroups)) {
+        // disallow non-admin to import the metadata of sentry
+        throw new SentryAccessDeniedException("Access denied to " + requestor
+            + " for export the metadata of sentry.");
+      }
+      TSentryMappingData tSentryMappingData = new TSentryMappingData();
+      Map<String, Set<TSentryPrivilege>> rolePrivileges =
+          sentryStore.getRoleNameTPrivilegesMap(databaseName, tableName);
+      tSentryMappingData.setRolePrivilegesMap(rolePrivileges);
+      Set<String> roleNames = rolePrivileges.keySet();
+      // roleNames should be null if databaseName == null and tableName == null
+      if (databaseName == null && tableName == null) {
+        roleNames = null;
+      }
+      List<Map<String, Set<String>>> mapList = sentryStore.getGroupUserRoleMapList(
+          roleNames);
+      tSentryMappingData.setGroupRolesMap(mapList.get(
+          SentryStore.INDEX_GROUP_ROLES_MAP));
+      tSentryMappingData.setUserRolesMap(mapList.get(SentryStore.INDEX_USER_ROLES_MAP));
+
+      response.setMappingData(tSentryMappingData);
+      response.setStatus(Status.OK());
+    } catch (Exception e) {
+      String msg = "Unknown error for request: " + request + ", message: " + e.getMessage();
+      LOGGER.error(msg, e);
+      response.setMappingData(new TSentryMappingData());
+      response.setStatus(Status.RuntimeError(msg, e));
+    }
+    return response;
+  }
+
+  // import the sentry mapping data
+  @Override
+  public TSentryImportMappingDataResponse import_sentry_mapping_data(
+      TSentryImportMappingDataRequest request) throws TException {
+    TSentryImportMappingDataResponse response = new TSentryImportMappingDataResponse();
+    try {
+      String requestor = request.getRequestorUserName();
+      Set<String> memberGroups = getRequestorGroups(requestor);
+      if (!inAdminGroups(memberGroups)) {
+        // disallow non-admin to import the metadata of sentry
+        throw new SentryAccessDeniedException("Access denied to " + requestor
+            + " for import the metadata of sentry.");
+      }
+      sentryStore.importSentryMetaData(request.getMappingData(), request.isOverwriteRole());
+      response.setStatus(Status.OK());
+    } catch (SentryInvalidInputException e) {
+      String msg = "Invalid input privilege object";
+      LOGGER.error(msg, e);
+      response.setStatus(Status.InvalidInput(msg, 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;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessorFactory.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessorFactory.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessorFactory.java
new file mode 100644
index 0000000..45966e5
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessorFactory.java
@@ -0,0 +1,40 @@
+/**
+ * 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 org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.service.thrift.ProcessorFactory;
+import org.apache.sentry.service.thrift.ServiceConstants;
+import org.apache.thrift.TMultiplexedProcessor;
+import org.apache.thrift.TProcessor;
+
+public class SentryPolicyStoreProcessorFactory extends ProcessorFactory {
+  public SentryPolicyStoreProcessorFactory(Configuration conf) {
+    super(conf);
+  }
+
+  public boolean register(TMultiplexedProcessor multiplexedProcessor) throws Exception {
+    SentryPolicyStoreProcessor sentryServiceHandler =
+        new SentryPolicyStoreProcessor(ServiceConstants.SENTRY_POLICY_SERVICE_NAME,
+            conf);
+    TProcessor processor =
+      new SentryProcessorWrapper<SentryPolicyService.Iface>(sentryServiceHandler);
+    multiplexedProcessor.registerProcessor(ServiceConstants.SENTRY_POLICY_SERVICE_NAME, processor);
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryProcessorWrapper.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryProcessorWrapper.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryProcessorWrapper.java
new file mode 100644
index 0000000..a5f11a9
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryProcessorWrapper.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.sentry.provider.db.service.thrift;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TProtocol;
+
+public class SentryProcessorWrapper<I extends SentryPolicyService.Iface> extends
+    SentryPolicyService.Processor<SentryPolicyService.Iface> {
+
+  public SentryProcessorWrapper(I iface) {
+    super(iface);
+  }
+
+  @Override
+  public boolean process(TProtocol in, TProtocol out) throws TException {
+    ThriftUtil.setIpAddress(in);
+    ThriftUtil.setImpersonator(in);
+    return super.process(in, out);
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryWebServer.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryWebServer.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryWebServer.java
new file mode 100644
index 0000000..a42f395
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryWebServer.java
@@ -0,0 +1,184 @@
+package org.apache.sentry.provider.db.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 com.codahale.metrics.servlets.AdminServlet;
+import com.google.common.base.Preconditions;
+
+import java.io.IOException;
+import java.util.EnumSet;
+import java.net.URL;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.SecurityUtil;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
+import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
+import org.eclipse.jetty.server.DispatcherType;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SentryWebServer {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(SentryWebServer.class);
+  private static final String RESOURCE_DIR = "/webapp";
+  private static final String WELCOME_PAGE = "SentryService.html";
+
+  private Server server;
+
+  public SentryWebServer(List<EventListener> listeners, int port, Configuration conf) {
+    server = new Server();
+
+    // Create a channel connector for "http/https" requests
+    SelectChannelConnector connector = new SelectChannelConnector();
+    if (conf.getBoolean(ServerConfig.SENTRY_WEB_USE_SSL, false)) {
+      SslContextFactory sslContextFactory = new SslContextFactory();
+      sslContextFactory.setKeyStorePath(conf.get(ServerConfig.SENTRY_WEB_SSL_KEYSTORE_PATH, ""));
+      sslContextFactory.setKeyStorePassword(
+          conf.get(ServerConfig.SENTRY_WEB_SSL_KEYSTORE_PASSWORD, ""));
+      // Exclude SSL blacklist protocols
+      sslContextFactory.setExcludeProtocols(ServerConfig.SENTRY_SSL_PROTOCOL_BLACKLIST_DEFAULT);
+      Set<String> moreExcludedSSLProtocols =
+          Sets.newHashSet(Splitter.on(",").trimResults().omitEmptyStrings()
+          .split(Strings.nullToEmpty(conf.get(ServerConfig.SENTRY_SSL_PROTOCOL_BLACKLIST))));
+      sslContextFactory.addExcludeProtocols(moreExcludedSSLProtocols.toArray(
+          new String[moreExcludedSSLProtocols.size()]));
+      connector = new SslSelectChannelConnector(sslContextFactory);
+      LOGGER.info("Now using SSL mode.");
+    }
+
+    connector.setPort(port);
+    server.addConnector(connector);
+
+    ServletContextHandler servletContextHandler = new ServletContextHandler();
+    ServletHolder servletHolder = new ServletHolder(AdminServlet.class);
+    servletContextHandler.addServlet(servletHolder, "/*");
+
+    for(EventListener listener:listeners) {
+      servletContextHandler.addEventListener(listener);
+    }
+
+    ServletHolder confServletHolder = new ServletHolder(ConfServlet.class);
+    servletContextHandler.addServlet(confServletHolder, "/conf");
+    servletContextHandler.getServletContext()
+        .setAttribute(ConfServlet.CONF_CONTEXT_ATTRIBUTE, conf);
+
+    ResourceHandler resourceHandler = new ResourceHandler();
+    resourceHandler.setDirectoriesListed(true);
+    URL url = this.getClass().getResource(RESOURCE_DIR);
+    try {
+      resourceHandler.setBaseResource(Resource.newResource(url.toString()));
+    } catch (IOException e) {
+      LOGGER.error("Got exception while setBaseResource for Sentry Service web UI", e);
+    }
+    resourceHandler.setWelcomeFiles(new String[]{WELCOME_PAGE});
+    ContextHandler contextHandler= new ContextHandler();
+    contextHandler.setHandler(resourceHandler);
+
+    ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection();
+    contextHandlerCollection.setHandlers(new Handler[]{contextHandler, servletContextHandler});
+
+    String authMethod = conf.get(ServerConfig.SENTRY_WEB_SECURITY_TYPE);
+    if (!ServerConfig.SENTRY_WEB_SECURITY_TYPE_NONE.equals(authMethod)) {
+      /**
+       * SentryAuthFilter is a subclass of AuthenticationFilter and
+       * AuthenticationFilter tagged as private and unstable interface:
+       * While there are not guarantees that this interface will not change,
+       * it is fairly stable and used by other projects (ie - Oozie)
+       */
+      FilterHolder filterHolder = servletContextHandler.addFilter(SentryAuthFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
+      filterHolder.setInitParameters(loadWebAuthenticationConf(conf));
+    }
+
+    server.setHandler(contextHandlerCollection);
+  }
+
+  public void start() throws Exception{
+    server.start();
+  }
+  public void stop() throws Exception{
+    server.stop();
+  }
+  public boolean isAlive() {
+    return server != null && server.isStarted();
+  }
+  private static Map<String, String> loadWebAuthenticationConf(Configuration conf) {
+    Map<String,String> prop = new HashMap<String, String>();
+    prop.put(AuthenticationFilter.CONFIG_PREFIX, ServerConfig.SENTRY_WEB_SECURITY_PREFIX);
+    String allowUsers = conf.get(ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS);
+    if (allowUsers == null || allowUsers.equals("")) {
+      allowUsers = conf.get(ServerConfig.ALLOW_CONNECT);
+      conf.set(ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS, allowUsers);
+    }
+    validateConf(conf);
+    for (Map.Entry<String, String> entry : conf) {
+      String name = entry.getKey();
+      if (name.startsWith(ServerConfig.SENTRY_WEB_SECURITY_PREFIX)) {
+        String value = conf.get(name);
+        prop.put(name, value);
+      }
+    }
+    return prop;
+  }
+
+  private static void validateConf(Configuration conf) {
+    String authHandlerName = conf.get(ServerConfig.SENTRY_WEB_SECURITY_TYPE);
+    Preconditions.checkNotNull(authHandlerName, "Web authHandler should not be null.");
+    String allowUsers = conf.get(ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS);
+    Preconditions.checkNotNull(allowUsers, "Allow connect user(s) should not be null.");
+    if (ServerConfig.SENTRY_WEB_SECURITY_TYPE_KERBEROS.equalsIgnoreCase(authHandlerName)) {
+      String principal = conf.get(ServerConfig.SENTRY_WEB_SECURITY_PRINCIPAL);
+      Preconditions.checkNotNull(principal, "Kerberos principal should not be null.");
+      Preconditions.checkArgument(principal.length() != 0, "Kerberos principal is not right.");
+      String keytabFile = conf.get(ServerConfig.SENTRY_WEB_SECURITY_KEYTAB);
+      Preconditions.checkNotNull(keytabFile, "Keytab File should not be null.");
+      Preconditions.checkArgument(keytabFile.length() != 0, "Keytab File is not right.");
+      try {
+        UserGroupInformation.setConfiguration(conf);
+        String hostPrincipal = SecurityUtil.getServerPrincipal(principal, ServerConfig.RPC_ADDRESS_DEFAULT);
+        UserGroupInformation.loginUserFromKeytab(hostPrincipal, keytabFile);
+      } catch (IOException ex) {
+        throw new IllegalArgumentException("Can't use Kerberos authentication, principal ["
+          + principal + "] keytab [" + keytabFile + "]", ex);
+      }
+      LOGGER.info("Using Kerberos authentication, principal ["
+          + principal + "] keytab [" + keytabFile + "]");
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/ThriftUtil.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/ThriftUtil.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/ThriftUtil.java
new file mode 100644
index 0000000..3a96d0b
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/thrift/ThriftUtil.java
@@ -0,0 +1,112 @@
+/**
+ * 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 org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TSaslClientTransport;
+import org.apache.thrift.transport.TSaslServerTransport;
+import org.apache.thrift.transport.TSocket;
+import org.apache.thrift.transport.TTransport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+public final class ThriftUtil {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(ThriftUtil.class);
+
+  public static void setImpersonator(final TProtocol in) {
+    try {
+      TTransport transport = in.getTransport();
+      if (transport instanceof TSaslServerTransport) {
+        String impersonator = ((TSaslServerTransport) transport).getSaslServer()
+            .getAuthorizationID();
+        setImpersonator(impersonator);
+      }
+    } catch (Exception e) {
+      // If there has exception when get impersonator info, log the error information.
+      LOGGER.warn("There is an error when get the impersonator:" + e.getMessage());
+    }
+  }
+
+  public static void setIpAddress(final TProtocol in) {
+    try {
+      TTransport transport = in.getTransport();
+      TSocket tSocket = getUnderlyingSocketFromTransport(transport);
+      if (tSocket != null) {
+        setIpAddress(tSocket.getSocket().getInetAddress().toString());
+      } else {
+        LOGGER.warn("Unknown Transport, cannot determine ipAddress");
+      }
+    } catch (Exception e) {
+      // If there has exception when get impersonator info, log the error information.
+      LOGGER.warn("There is an error when get the client's ip address:" + e.getMessage());
+    }
+  }
+
+  /**
+   * Returns the underlying TSocket from the transport, or null of the transport type is unknown.
+   */
+  private static TSocket getUnderlyingSocketFromTransport(TTransport transport) {
+    Preconditions.checkNotNull(transport);
+    if (transport instanceof TSaslServerTransport) {
+      return (TSocket) ((TSaslServerTransport) transport).getUnderlyingTransport();
+    } else if (transport instanceof TSaslClientTransport) {
+      return (TSocket) ((TSaslClientTransport) transport).getUnderlyingTransport();
+    } else if (transport instanceof TSocket) {
+      return (TSocket) transport;
+    }
+    return null;
+  }
+
+  private static ThreadLocal<String> threadLocalIpAddress = new ThreadLocal<String>() {
+    @Override
+    protected synchronized String initialValue() {
+      return "";
+    }
+  };
+
+  public static void setIpAddress(String ipAddress) {
+    threadLocalIpAddress.set(ipAddress);
+  }
+
+  public static String getIpAddress() {
+    return threadLocalIpAddress.get();
+  }
+
+  private static ThreadLocal<String> threadLocalImpersonator = new ThreadLocal<String>() {
+    @Override
+    protected synchronized String initialValue() {
+      return "";
+    }
+  };
+
+  public static void setImpersonator(String impersonator) {
+    threadLocalImpersonator.set(impersonator);
+  }
+
+  public static String getImpersonator() {
+    return threadLocalImpersonator.get();
+  }
+  
+  private ThriftUtil() {
+    // Make constructor private to avoid instantiation
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/sentry/blob/e72e6eac/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaHelper.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaHelper.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaHelper.java
new file mode 100644
index 0000000..cf1c725
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaHelper.java
@@ -0,0 +1,315 @@
+/**
+ * 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.tools;
+
+import java.util.IllegalFormatException;
+
+public final class SentrySchemaHelper {
+  public static final String DB_DERBY = "derby";
+  public static final String DB_MYSQL = "mysql";
+  public static final String DB_POSTGRACE = "postgres";
+  public static final String DB_ORACLE = "oracle";
+  public static final String DB_DB2 = "db2";
+
+  public interface NestedScriptParser {
+
+    public enum CommandType {
+      PARTIAL_STATEMENT,
+      TERMINATED_STATEMENT,
+      COMMENT
+    }
+
+    String DEFAUTL_DELIMITER = ";";
+    /***
+     * Find the type of given command
+     * @param dbCommand
+     * @return
+     */
+    boolean isPartialCommand(String dbCommand) throws IllegalArgumentException;
+
+    /** Parse the DB specific nesting format and extract the inner script name if any
+     * @param dbCommand command from parent script
+     * @return
+     * @throws IllegalFormatException
+     */
+    String getScriptName(String dbCommand) throws IllegalArgumentException;
+
+    /***
+     * Find if the given command is a nested script execution
+     * @param dbCommand
+     * @return
+     */
+    boolean isNestedScript(String dbCommand);
+
+    /***
+     * Find if the given command is should be passed to DB
+     * @param dbCommand
+     * @return
+     */
+    boolean isNonExecCommand(String dbCommand);
+
+    /***
+     * Get the SQL statement delimiter
+     * @return
+     */
+    String getDelimiter();
+
+    /***
+     * Clear any client specific tags
+     * @return
+     */
+    String cleanseCommand(String dbCommand);
+
+    /***
+     * Does the DB required table/column names quoted
+     * @return
+     */
+    boolean needsQuotedIdentifier();
+
+    /***
+     * Set DB specific options if any
+     * @param dbOps
+     */
+    void setDbOpts(String dbOps);
+  }
+
+
+  /***
+   * Base implemenation of NestedScriptParser
+   * abstractCommandParser.
+   *
+   */
+  private static abstract class AbstractCommandParser implements NestedScriptParser {
+    private String dbOpts = null;
+
+    @Override
+    public boolean isPartialCommand(String dbCommand) throws IllegalArgumentException{
+      if (dbCommand == null || dbCommand.isEmpty()) {
+        throw new IllegalArgumentException("invalid command line " + dbCommand);
+      }
+      String trimmedDbCommand = dbCommand.trim();
+      return !(trimmedDbCommand.endsWith(getDelimiter()) || isNonExecCommand(trimmedDbCommand));
+    }
+
+    @Override
+    public boolean isNonExecCommand(String dbCommand) {
+      return dbCommand.startsWith("--") || dbCommand.startsWith("#");
+    }
+
+    @Override
+    public String getDelimiter() {
+      return DEFAUTL_DELIMITER;
+    }
+
+    @Override
+    public String cleanseCommand(String dbCommand) {
+      // strip off the delimiter
+      if (dbCommand.endsWith(getDelimiter())) {
+        dbCommand = dbCommand.substring(0,
+            dbCommand.length() - getDelimiter().length());
+      }
+      return dbCommand;
+    }
+
+    @Override
+    public boolean needsQuotedIdentifier() {
+      return false;
+    }
+
+    @Override
+    public void setDbOpts(String dbOpts) {
+      this.dbOpts = dbOpts;
+    }
+
+    protected String getDbOpts() {
+      return dbOpts;
+    }
+  }
+
+
+  // Derby commandline parser
+  public static class DerbyCommandParser extends AbstractCommandParser {
+    private static final String DERBY_NESTING_TOKEN = "RUN";
+
+    @Override
+    public String getScriptName(String dbCommand) throws IllegalArgumentException {
+
+      if (!isNestedScript(dbCommand)) {
+        throw new IllegalArgumentException("Not a script format " + dbCommand);
+      }
+      String[] tokens = dbCommand.split(" ");
+      if (tokens.length != 2) {
+        throw new IllegalArgumentException("Couldn't parse line " + dbCommand);
+      }
+      return tokens[1].replace(";", "").replaceAll("'", "");
+    }
+
+    @Override
+    public boolean isNestedScript(String dbCommand) {
+      // Derby script format is RUN '<file>'
+     return dbCommand.startsWith(DERBY_NESTING_TOKEN);
+    }
+  }
+
+
+  // MySQL parser
+  public static class MySqlCommandParser extends AbstractCommandParser {
+    private static final String MYSQL_NESTING_TOKEN = "SOURCE";
+    private static final String DELIMITER_TOKEN = "DELIMITER";
+    private String delimiter = DEFAUTL_DELIMITER;
+
+    @Override
+    public boolean isPartialCommand(String dbCommand) throws IllegalArgumentException{
+      boolean isPartial = super.isPartialCommand(dbCommand);
+      // if this is a delimiter directive, reset our delimiter
+      if (dbCommand.startsWith(DELIMITER_TOKEN)) {
+        String[] tokens = dbCommand.split(" ");
+        if (tokens.length != 2) {
+          throw new IllegalArgumentException("Couldn't parse line " + dbCommand);
+        }
+        delimiter = tokens[1];
+      }
+      return isPartial;
+    }
+
+    @Override
+    public String getScriptName(String dbCommand) throws IllegalArgumentException {
+      String[] tokens = dbCommand.split(" ");
+      if (tokens.length != 2) {
+        throw new IllegalArgumentException("Couldn't parse line " + dbCommand);
+      }
+      // remove ending ';'
+      return tokens[1].replace(";", "");
+    }
+
+    @Override
+    public boolean isNestedScript(String dbCommand) {
+      return dbCommand.startsWith(MYSQL_NESTING_TOKEN);
+    }
+
+    @Override
+    public String getDelimiter() {
+      return delimiter;
+    }
+
+    @Override
+    public boolean isNonExecCommand(String dbCommand) {
+      return super.isNonExecCommand(dbCommand) ||
+          dbCommand.startsWith("/*") && dbCommand.endsWith("*/") ||
+          dbCommand.startsWith(DELIMITER_TOKEN);
+    }
+
+    @Override
+    public String cleanseCommand(String dbCommand) {
+      return super.cleanseCommand(dbCommand).replaceAll("/\\*.*?\\*/[^;]", "");
+    }
+
+  }
+
+  // Postgres specific parser
+  public static class PostgresCommandParser extends AbstractCommandParser {
+    public static final String POSTGRES_STRING_COMMAND_FILTER = "SET standard_conforming_strings";
+    public static final String POSTGRES_STRING_CLIENT_ENCODING = "SET client_encoding";
+    public static final String POSTGRES_SKIP_STANDARD_STRING = "postgres.filter.81";
+    private static final String POSTGRES_NESTING_TOKEN = "\\i";
+
+    @Override
+    public String getScriptName(String dbCommand) throws IllegalArgumentException {
+      String[] tokens = dbCommand.split(" ");
+      if (tokens.length != 2) {
+        throw new IllegalArgumentException("Couldn't parse line " + dbCommand);
+      }
+      // remove ending ';'
+      return tokens[1].replace(";", "");
+    }
+
+    @Override
+    public boolean isNestedScript(String dbCommand) {
+      return dbCommand.startsWith(POSTGRES_NESTING_TOKEN);
+    }
+
+    @Override
+    public boolean needsQuotedIdentifier() {
+      return true;
+    }
+
+    @Override
+    public boolean isNonExecCommand(String dbCommand) {
+      // Skip "standard_conforming_strings" command which is not supported in older postgres
+      if (POSTGRES_SKIP_STANDARD_STRING.equalsIgnoreCase(getDbOpts()) 
+        && (dbCommand.startsWith(POSTGRES_STRING_COMMAND_FILTER) || dbCommand.startsWith(POSTGRES_STRING_CLIENT_ENCODING))) {
+        return true;
+      }
+      return super.isNonExecCommand(dbCommand);
+    }
+  }
+
+  //Oracle specific parser
+  public static class OracleCommandParser extends AbstractCommandParser {
+    private static final String ORACLE_NESTING_TOKEN = "@";
+    @Override
+    public String getScriptName(String dbCommand) throws IllegalArgumentException {
+      if (!isNestedScript(dbCommand)) {
+        throw new IllegalArgumentException("Not a nested script format " + dbCommand);
+      }
+      // remove ending ';' and starting '@'
+      return dbCommand.replace(";", "").replace(ORACLE_NESTING_TOKEN, "");
+    }
+
+    @Override
+    public boolean isNestedScript(String dbCommand) {
+      return dbCommand.startsWith(ORACLE_NESTING_TOKEN);
+    }
+  }
+
+  // DB2 commandline parser
+  public static class DB2CommandParser extends AbstractCommandParser {
+
+    @Override
+    public String getScriptName(String dbCommand) throws IllegalArgumentException {
+        //DB2 does not support nesting script
+        throw new IllegalArgumentException("DB2 does not support nesting script " + dbCommand);
+    }
+
+    @Override
+    public boolean isNestedScript(String dbCommand) {
+        //DB2 does not support nesting script
+     return false;
+    }
+  }
+
+  public static NestedScriptParser getDbCommandParser(String dbName) {
+    if (dbName.equalsIgnoreCase(DB_DERBY)) {
+      return new DerbyCommandParser();
+    } else if (dbName.equalsIgnoreCase(DB_MYSQL)) {
+      return new MySqlCommandParser();
+    } else if (dbName.equalsIgnoreCase(DB_POSTGRACE)) {
+      return new PostgresCommandParser();
+    } else if (dbName.equalsIgnoreCase(DB_ORACLE)) {
+        return new OracleCommandParser();
+    } else if (dbName.equalsIgnoreCase(DB_DB2)) {
+      return new DB2CommandParser();
+    } else {
+      throw new IllegalArgumentException("Unknown dbType " + dbName);
+    }
+  }
+  
+  private SentrySchemaHelper() {
+    // Make constructor private to avoid instantiation
+  }
+}