sentry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From br...@apache.org
Subject git commit: SENTRY-3: Create a diagnostics tool for configuration validation (Prasad via Brock)
Date Mon, 24 Feb 2014 23:07:56 GMT
Repository: incubator-sentry
Updated Branches:
  refs/heads/master cf798ac71 -> a4819f5b7


SENTRY-3: Create a diagnostics tool for configuration validation (Prasad via Brock)


Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/a4819f5b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/a4819f5b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/a4819f5b

Branch: refs/heads/master
Commit: a4819f5b7b8ce5fb9f3c9b94c6dbb1e2dfbe1f78
Parents: cf798ac
Author: Brock Noland <brock@apache.org>
Authored: Mon Feb 24 17:06:07 2014 -0600
Committer: Brock Noland <brock@apache.org>
Committed: Mon Feb 24 17:06:07 2014 -0600

----------------------------------------------------------------------
 bin/config-tool.sh                              |  25 +
 bin/sentry                                      |  65 +++
 .../binding/hive/HiveAuthzBindingHook.java      |  12 +-
 .../binding/hive/authz/HiveAuthzBinding.java    |   9 +-
 .../binding/hive/authz/SentryConfigTool.java    | 510 +++++++++++++++++++
 .../sentry/binding/hive/conf/HiveAuthzConf.java |  51 ++
 sentry-core/sentry-core-common/pom.xml          |   4 +
 .../common/SentryConfigurationException.java    |  67 +++
 .../sentry/policy/common/PolicyEngine.java      |  13 +-
 .../sentry/policy/db/SimpleDBPolicyEngine.java  |  37 +-
 .../policy/search/SimpleSearchPolicyEngine.java |  22 +-
 .../provider/common/AuthorizationProvider.java  |  30 ++
 .../common/NoAuthorizationProvider.java         |  26 +
 .../sentry/provider/common/ProviderBackend.java |   7 +
 .../file/ResourceAuthorizationProvider.java     |  57 ++-
 .../file/SimpleFileProviderBackend.java         |  79 ++-
 .../provider/file/TestGetGroupMapping.java      |  21 +
 .../apache/sentry/tests/e2e/hive/Context.java   |   6 +-
 .../sentry/tests/e2e/hive/TestConfigTool.java   | 304 +++++++++++
 19 files changed, 1315 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/bin/config-tool.sh
----------------------------------------------------------------------
diff --git a/bin/config-tool.sh b/bin/config-tool.sh
new file mode 100755
index 0000000..b286421
--- /dev/null
+++ b/bin/config-tool.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+# 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.
+
+_CMD_JAR=sentry-binding-hive-*.jar
+_HIVE_CMD=${HIVE_HOME}/bin/hive
+for f in ${SENTRY_HOME}/lib/*.jar; do
+  HADOOP_CLASSPATH=${HADOOP_CLASSPATH}:${f}
+done
+
+${_HIVE_CMD} --service jar ${SENTRY_HOME}/lib/${_CMD_JAR} org.apache.sentry.binding.hive.authz.SentryConfigTool "$@"
+

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/bin/sentry
----------------------------------------------------------------------
diff --git a/bin/sentry b/bin/sentry
new file mode 100755
index 0000000..9f2ce77
--- /dev/null
+++ b/bin/sentry
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+
+# 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.
+
+bin=`dirname "$0"`
+myhome=`cd "$bin/.."; pwd`
+
+# the root of the Hive installation
+if [[ -z $SENTRY_HOME ]] ; then
+  export SENTRY_HOME=$myhome
+fi
+
+_HIVE_CMD=hive
+#check to see if the hive conf dir is given as an optional argument
+while [ $# -gt 0 ]; do    # Until you run out of parameters . . .
+  case "$1" in
+    --hive-config)
+        shift
+        confdir=$1
+        shift
+        export HIVE_CONF_DIR=$confdir
+        echo Using hive-conf-dir $HIVE_CONF_DIR
+        ;;
+    --hive-home)
+        shift
+        homedir=$1
+        shift
+        export HIVE_HOME=$homedir
+        echo Using hive-home $HIVE_HOME
+        ;;
+    --command)
+        shift
+        case "$1" in
+          config-tool)
+             shift
+             $SENTRY_HOME/bin/config-tool.sh "$@"
+             ;;
+          *)
+             echo Unknown option $1
+             echo "Usage sentry --command <config-tool [config-tool-options]>"
+             break
+             ;;
+        esac
+        break
+        ;;
+    *)
+        echo "Usage sentry --command <config-tool [config-tool-options]>"
+        break
+        ;;
+  esac
+done
+

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
----------------------------------------------------------------------
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
index cac4864..3624e8f 100644
--- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
@@ -277,7 +277,17 @@ implements HiveDriverFilterHook {
       authorizeWithHiveBindings(context, stmtAuthObject, stmtOperation);
     } catch (AuthorizationException e) {
       executeOnFailureHooks(context, stmtOperation, e);
-      throw new SemanticException("No valid privileges", e);
+      String permsRequired = "";
+      for (String perm : hiveAuthzBinding.getLastQueryPermissionErrors()) {
+        permsRequired += perm + ";";
+      }
+      context.getConf().set(HiveAuthzConf.HIVE_SENTRY_AUTH_ERRORS, permsRequired);
+      throw new SemanticException(HiveAuthzConf.HIVE_SENTRY_PRIVILEGE_ERROR_MESSAGE, e);
+    }
+    if ("true".equalsIgnoreCase(context.getConf().
+        get(HiveAuthzConf.HIVE_SENTRY_MOCK_COMPILATION))) {
+      throw new SemanticException(HiveAuthzConf.HIVE_SENTRY_MOCK_ERROR + " Mock query compilation aborted. Set " +
+          HiveAuthzConf.HIVE_SENTRY_MOCK_COMPILATION + " to 'false' for normal query processing");
     }
     hiveAuthzBinding.set(context.getConf());
   }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzBinding.java
----------------------------------------------------------------------
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzBinding.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzBinding.java
index 45d5d3b..f6a1ecc 100644
--- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzBinding.java
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzBinding.java
@@ -63,7 +63,7 @@ public class HiveAuthzBinding {
   public HiveAuthzBinding (HiveConf hiveConf, HiveAuthzConf authzConf) throws Exception {
     this.authzConf = authzConf;
     this.authServer = new Server(authzConf.get(AuthzConfVars.AUTHZ_SERVER_NAME.getVar()));
-    this.authProvider = getAuthProvider(hiveConf, authServer.getName());
+    this.authProvider = getAuthProvider(hiveConf, authzConf, authServer.getName());
   }
 
   /**
@@ -102,7 +102,8 @@ public class HiveAuthzBinding {
   }
 
   // Instantiate the configured authz provider
-  private AuthorizationProvider getAuthProvider(HiveConf hiveConf, String serverName) throws Exception {
+  public static AuthorizationProvider getAuthProvider(HiveConf hiveConf, HiveAuthzConf authzConf,
+        String serverName) throws Exception {
     boolean isTestingMode = Boolean.parseBoolean(Strings.nullToEmpty(
         authzConf.get(AuthzConfVars.SENTRY_TESTING_MODE.getVar())).trim());
     LOG.debug("Testing mode is " + isTestingMode);
@@ -232,4 +233,8 @@ public class HiveAuthzBinding {
   private AuthorizableType getAuthzType (List<DBModelAuthorizable> hierarchy){
     return hierarchy.get(hierarchy.size() -1).getAuthzType();
   }
+
+  public List<String> getLastQueryPermissionErrors() {
+    return authProvider.getLastFailedPermissions();
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/SentryConfigTool.java
----------------------------------------------------------------------
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/SentryConfigTool.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/SentryConfigTool.java
new file mode 100644
index 0000000..d7a518d
--- /dev/null
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/SentryConfigTool.java
@@ -0,0 +1,510 @@
+/*
+ * 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.binding.hive.authz;
+
+import java.security.CodeSource;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Set;
+
+import org.apache.sentry.binding.hive.HiveAuthzBindingSessionHook;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionGroup;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.Parser;
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
+import org.apache.hadoop.hive.ql.Driver;
+import org.apache.hadoop.hive.ql.parse.SemanticException;
+import org.apache.hadoop.hive.ql.processors.CommandProcessorResponse;
+import org.apache.hadoop.hive.ql.session.SessionState;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.sentry.binding.hive.HiveAuthzBindingHook;
+import org.apache.sentry.binding.hive.conf.HiveAuthzConf;
+import org.apache.sentry.binding.hive.conf.HiveAuthzConf.AuthzConfVars;
+import org.apache.sentry.provider.common.AuthorizationProvider;
+import org.apache.sentry.core.common.SentryConfigurationException;
+import org.apache.sentry.core.model.db.Server;
+import org.apache.sentry.core.common.Subject;
+
+public class SentryConfigTool {
+  private String sentrySiteFile = null;
+  private String policyFile = null;
+  private String query = null;
+  private String jdbcURL = null;
+  private String user = null;
+  private String passWord = null;
+  private boolean listPerms = false;
+  private boolean validate = false;
+  private HiveConf hiveConf = null;
+  private HiveAuthzConf authzConf = null;
+  private AuthorizationProvider sentryProvider = null;
+
+  public SentryConfigTool() {
+
+  }
+
+  public AuthorizationProvider getSentryProvider() {
+    return sentryProvider;
+  }
+
+  public void setSentryProvider(AuthorizationProvider sentryProvider) {
+    this.sentryProvider = sentryProvider;
+  }
+
+  public HiveConf getHiveConf() {
+    return hiveConf;
+  }
+
+  public void setHiveConf(HiveConf hiveConf) {
+    this.hiveConf = hiveConf;
+  }
+
+  public HiveAuthzConf getAuthzConf() {
+    return authzConf;
+  }
+
+  public void setAuthzConf(HiveAuthzConf authzConf) {
+    this.authzConf = authzConf;
+  }
+
+  public boolean isValidate() {
+    return validate;
+  }
+
+  public void setValidate(boolean validate) {
+    this.validate = validate;
+  }
+
+  public String getSentrySiteFile() {
+    return sentrySiteFile;
+  }
+
+  public void setSentrySiteFile(String sentrySiteFile) {
+    this.sentrySiteFile = sentrySiteFile;
+  }
+
+  public String getPolicyFile() {
+    return policyFile;
+  }
+
+  public void setPolicyFile(String policyFile) {
+    this.policyFile = policyFile;
+  }
+
+  public String getQuery() {
+    return query;
+  }
+
+  public void setQuery(String query) {
+    this.query = query;
+  }
+
+  public String getJdbcURL() {
+    return jdbcURL;
+  }
+
+  public void setJdbcURL(String jdbcURL) {
+    this.jdbcURL = jdbcURL;
+  }
+
+  public String getUser() {
+    return user;
+  }
+
+  public void setUser(String user) {
+    this.user = user;
+  }
+
+  public String getPassWord() {
+    return passWord;
+  }
+
+  public void setPassWord(String passWord) {
+    this.passWord = passWord;
+  }
+
+  public boolean isListPerms() {
+    return listPerms;
+  }
+
+  public void setListPerms(boolean listPerms) {
+    this.listPerms = listPerms;
+  }
+
+  /**
+   * set the required system property to be read by HiveConf and AuthzConf
+   * @throws Exception
+   */
+  public void setupConfig() throws Exception {
+    System.out.println("Configuration: ");
+    CodeSource src = SentryConfigTool.class.getProtectionDomain()
+        .getCodeSource();
+    if (src != null) {
+      System.out.println("Sentry package jar: " + src.getLocation());
+    }
+
+    if (getPolicyFile() != null) {
+      System.setProperty(AuthzConfVars.AUTHZ_PROVIDER_RESOURCE.getVar(),
+          getPolicyFile());
+    }
+    System.setProperty(AuthzConfVars.SENTRY_TESTING_MODE.getVar(), "true");
+    setHiveConf(new HiveConf(SessionState.class));
+    getHiveConf().setVar(ConfVars.SEMANTIC_ANALYZER_HOOK,
+        HiveAuthzBindingHook.class.getName());
+    try {
+      System.out.println("Hive config: " + getHiveConf().getHiveSiteLocation());
+    } catch (NullPointerException e) {
+      // Hack, hiveConf doesn't provide a reliable way check if it found a valid
+      // hive-site
+      throw new SentryConfigurationException("Didn't find a hive-site.xml");
+
+    }
+
+    if (getSentrySiteFile() != null) {
+      getHiveConf()
+          .set(HiveAuthzConf.HIVE_SENTRY_CONF_URL, getSentrySiteFile());
+    }
+
+    setAuthzConf(HiveAuthzConf.getAuthzConf(getHiveConf()));
+    System.out.println("Sentry config: "
+        + getAuthzConf().getHiveAuthzSiteFile());
+    System.out.println("Sentry Policy: "
+        + getAuthzConf().get(AuthzConfVars.AUTHZ_PROVIDER_RESOURCE.getVar()));
+    System.out.println("Sentry server: "
+        + getAuthzConf().get(AuthzConfVars.AUTHZ_SERVER_NAME.getVar()));
+
+    setSentryProvider(getAuthorizationProvider());
+  }
+
+  // load auth provider
+  private AuthorizationProvider getAuthorizationProvider()
+      throws IllegalStateException, SentryConfigurationException {
+    String serverName = new Server(getAuthzConf().get(
+        AuthzConfVars.AUTHZ_SERVER_NAME.getVar())).getName();
+    // get the configured sentry provider
+    AuthorizationProvider sentryProvider = null;
+    try {
+      sentryProvider = HiveAuthzBinding.getAuthProvider(getHiveConf(),
+          authzConf, serverName);
+    } catch (SentryConfigurationException eC) {
+      printConfigErrors(eC);
+    } catch (Exception e) {
+      throw new IllegalStateException("Couldn't load sentry provider ", e);
+    }
+    return sentryProvider;
+  }
+
+  // validate policy files
+  public void validatePolicy() throws Exception {
+    try {
+      getSentryProvider().validateResource(true);
+    } catch (SentryConfigurationException e) {
+      printConfigErrors(e);
+    }
+    System.out.println("No errors found in the policy file");
+  }
+
+  // list permissions for given user
+  public void listPerms() throws Exception {
+    getSentryProvider().validateResource(true);
+    System.out.println("Available privileges for user " + getUser() + ":");
+    Set<String> permList = getSentryProvider().listPermissionsForSubject(
+        new Subject(getUser()));
+    for (String perms : permList) {
+      System.out.println("\t" + perms);
+    }
+    if (permList.isEmpty()) {
+      System.out.println("\t*** No permissions available ***");
+    }
+  }
+
+  // Verify the given query
+  public void verifyLocalQuery(String queryStr) throws Exception {
+    // setup Hive driver
+    SessionState session = new SessionState(getHiveConf());
+    SessionState.start(session);
+    Driver driver = new Driver(session.getConf(), getUser(), null);
+
+    // compile the query
+    CommandProcessorResponse compilerStatus = driver
+        .compileAndRespond(queryStr);
+    if (compilerStatus.getResponseCode() != 0) {
+      String errMsg = compilerStatus.getErrorMessage();
+      if (errMsg.contains(HiveAuthzConf.HIVE_SENTRY_PRIVILEGE_ERROR_MESSAGE)) {
+        printMissingPerms(getHiveConf().get(
+            HiveAuthzConf.HIVE_SENTRY_AUTH_ERRORS));
+      }
+      throw new SemanticException("Compilation error: "
+          + compilerStatus.getErrorMessage());
+    }
+    driver.close();
+    System.out
+        .println("User " + getUser() + " has privileges to run the query");
+  }
+
+  // connect to remote HS2 and run mock query
+  public void verifyRemoteQuery(String queryStr) throws Exception {
+    Class.forName("org.apache.hive.jdbc.HiveDriver");
+    Connection conn = DriverManager.getConnection(getJdbcURL(), getUser(),
+        getPassWord());
+    Statement stmt = conn.createStatement();
+    if (!isSentryEnabledOnHiveServer(stmt)) {
+      throw new IllegalStateException("Sentry is not enabled on HiveServer2");
+    }
+    stmt.execute("set " + HiveAuthzConf.HIVE_SENTRY_MOCK_COMPILATION + "=true");
+    try {
+      stmt.execute(queryStr);
+    } catch (SQLException e) {
+      String errMsg = e.getMessage();
+      if (errMsg.contains(HiveAuthzConf.HIVE_SENTRY_MOCK_ERROR)) {
+        System.out.println("User "
+            + readConfig(stmt, HiveAuthzConf.HIVE_SENTRY_SUBJECT_NAME)
+            + " has privileges to run the query");
+        return;
+      } else if (errMsg
+          .contains(HiveAuthzConf.HIVE_SENTRY_PRIVILEGE_ERROR_MESSAGE)) {
+        printMissingPerms(readConfig(stmt,
+            HiveAuthzConf.HIVE_SENTRY_AUTH_ERRORS));
+        throw e;
+      } else {
+        throw e;
+      }
+    } finally {
+      if (!stmt.isClosed()) {
+        stmt.close();
+      }
+      conn.close();
+    }
+
+  }
+
+  // verify senty session hook is set
+  private boolean isSentryEnabledOnHiveServer(Statement stmt)
+      throws SQLException {
+    return HiveAuthzBindingSessionHook.class.getName().equalsIgnoreCase(
+        readConfig(stmt, HiveConf.ConfVars.HIVE_SERVER2_SESSION_HOOK.varname));
+  }
+
+  // read a config value using 'set' statement
+  private String readConfig(Statement stmt, String configKey)
+      throws SQLException {
+    ResultSet res = stmt.executeQuery("set " + configKey);
+    if (!res.next()) {
+      return null;
+    }
+    // parse key=value result format
+    String result = res.getString(1);
+    res.close();
+    return result.substring(result.indexOf("=") + 1);
+  }
+
+  // print configuration/policy file errors and warnings
+  private void printConfigErrors(SentryConfigurationException configException)
+      throws SentryConfigurationException {
+    System.out.println(" *** Found configuration problems *** ");
+    for (String errMsg : configException.getConfigErrors()) {
+      System.out.println("ERROR: " + errMsg);
+    }
+    for (String warnMsg : configException.getConfigWarnings()) {
+      System.out.println("Warning: " + warnMsg);
+    }
+    throw configException;
+  }
+
+  // extract the authorization errors from config property and print
+  private void printMissingPerms(String errMsg) {
+    if (errMsg == null || errMsg.isEmpty()) {
+      return;
+    }
+    System.out.println("*** Query compilation failed ***");
+    String perms[] = errMsg.replaceFirst(
+        ".*" + HiveAuthzConf.HIVE_SENTRY_PRIVILEGE_ERROR_MESSAGE, "")
+        .split(";");
+    System.out.println("Required privileges for given query:");
+    for (int count = 0; count < perms.length; count++) {
+      System.out.println(" \t " + perms[count]);
+    }
+  }
+
+  // print usage
+  private void usage(Options sentryOptions) {
+    HelpFormatter formatter = new HelpFormatter();
+    formatter.printHelp("Sentry", sentryOptions);
+    System.exit(-1);
+  }
+
+  /**
+   *  parse arguments
+   *
+   *   -d,--debug               enable debug output
+   *   -e,--query <arg>         Query privilege verification, requires -u
+   *    -h,--help                Print usage
+   *   -i,--policyIni <arg>     Policy file path
+   *   -j,--jdbcURL <arg>       JDBC URL
+   *   -l,--listPerms           list permissions for given user, requires -u
+   *   -p,--password <arg>      Password
+   *   -s,--sentry-site <arg>   sentry-site file path
+   *   -u,--user <arg>          user name
+   *   -v,--validate            Validate policy file
+   * @param args
+   */
+  private void parseArgs(String[] args) {
+    boolean enableDebug = false;
+
+    Options sentryOptions = new Options();
+
+    Option helpOpt = new Option("h", "help", false, "Print usage");
+    helpOpt.setRequired(false);
+
+    Option validateOpt = new Option("v", "validate", false,
+        "Validate policy file");
+    validateOpt.setRequired(false);
+
+    Option queryOpt = new Option("e", "query", true,
+        "Query privilege verification, requires -u");
+    queryOpt.setRequired(false);
+
+    Option listPermsOpt = new Option("l", "listPerms", false,
+        "list permissions for given user, requires -u");
+    listPermsOpt.setRequired(false);
+
+    // required args
+    OptionGroup sentryOptGroup = new OptionGroup();
+    sentryOptGroup.addOption(helpOpt);
+    sentryOptGroup.addOption(validateOpt);
+    sentryOptGroup.addOption(queryOpt);
+    sentryOptGroup.addOption(listPermsOpt);
+    sentryOptGroup.setRequired(true);
+    sentryOptions.addOptionGroup(sentryOptGroup);
+
+    // optional args
+    Option jdbcArg = new Option("j", "jdbcURL", true, "JDBC URL");
+    jdbcArg.setRequired(false);
+    sentryOptions.addOption(jdbcArg);
+
+    Option sentrySitePath = new Option("s", "sentry-site", true,
+        "sentry-site file path");
+    sentrySitePath.setRequired(false);
+    sentryOptions.addOption(sentrySitePath);
+
+    Option globalPolicyPath = new Option("i", "policyIni", true,
+        "Policy file path");
+    globalPolicyPath.setRequired(false);
+    sentryOptions.addOption(globalPolicyPath);
+
+    Option userOpt = new Option("u", "user", true, "user name");
+    userOpt.setRequired(false);
+    sentryOptions.addOption(userOpt);
+
+    Option passWordOpt = new Option("p", "password", true, "Password");
+    userOpt.setRequired(false);
+    sentryOptions.addOption(passWordOpt);
+
+    Option debugOpt = new Option("d", "debug", false, "enable debug output");
+    debugOpt.setRequired(false);
+    sentryOptions.addOption(debugOpt);
+
+    try {
+      Parser parser = new GnuParser();
+      CommandLine cmd = parser.parse(sentryOptions, args);
+
+      for (Option opt : cmd.getOptions()) {
+        if (opt.getOpt().equals("s")) {
+          setSentrySiteFile(opt.getValue());
+        } else if (opt.getOpt().equals("i")) {
+          setPolicyFile(opt.getValue());
+        } else if (opt.getOpt().equals("e")) {
+          setQuery(opt.getValue());
+        } else if (opt.getOpt().equals("j")) {
+          setJdbcURL(opt.getValue());
+        } else if (opt.getOpt().equals("u")) {
+          setUser(opt.getValue());
+        } else if (opt.getOpt().equals("p")) {
+          setPassWord(opt.getValue());
+        } else if (opt.getOpt().equals("l")) {
+          setListPerms(true);
+        } else if (opt.getOpt().equals("v")) {
+          setValidate(true);
+        } else if (opt.getOpt().equals("h")) {
+          usage(sentryOptions);
+        } else if (opt.getOpt().equals("d")) {
+          enableDebug = true;
+        }
+      }
+
+      if (isListPerms() && (getUser() == null)) {
+        throw new ParseException("Can't use -l without -u ");
+      }
+      if ((getQuery() != null) && (getUser() == null)) {
+        throw new ParseException("Must use -u with -e ");
+      }
+    } catch (ParseException e1) {
+      System.out.println("Argument parsing error: " + e1.getMessage());
+      usage(sentryOptions);
+    }
+
+    if (!enableDebug) {
+      // turn off log
+      LogManager.getRootLogger().setLevel(Level.OFF);
+    }
+  }
+
+  public static void main(String args[]) throws Exception {
+    SentryConfigTool sentryTool = new SentryConfigTool();
+
+    try {
+      // parse arguments
+      sentryTool.parseArgs(args);
+
+      // load configuration
+      sentryTool.setupConfig();
+
+      // validate configuration
+      if (sentryTool.isValidate()) {
+        sentryTool.validatePolicy();
+      }
+
+      // list permissions for give user
+      if (sentryTool.isListPerms()) {
+        sentryTool.listPerms();
+      }
+
+      // verify given query
+      if (sentryTool.getQuery() != null) {
+        if (sentryTool.getJdbcURL() != null) {
+          sentryTool.verifyRemoteQuery(sentryTool.getQuery());
+        } else {
+          sentryTool.verifyLocalQuery(sentryTool.getQuery());
+        }
+      }
+    } catch (Exception e) {
+      System.out.println("Sentry tool reported Errors: " + e.getMessage());
+      System.exit(1);
+    }
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/conf/HiveAuthzConf.java
----------------------------------------------------------------------
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/conf/HiveAuthzConf.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/conf/HiveAuthzConf.java
index b7d79d6..c4f12b5 100644
--- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/conf/HiveAuthzConf.java
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/conf/HiveAuthzConf.java
@@ -16,12 +16,14 @@
  */
 package org.apache.sentry.binding.hive.conf;
 
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hive.conf.HiveConf;
 import org.mortbay.log.Log;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,6 +38,10 @@ public class HiveAuthzConf extends Configuration {
   public static final String HIVE_SENTRY_CONF_URL = "hive.sentry.conf.url";
   public static final String HIVE_ACCESS_SUBJECT_NAME = "hive.access.subject.name";
   public static final String HIVE_SENTRY_SUBJECT_NAME = "hive.sentry.subject.name";
+  public static final String HIVE_SENTRY_AUTH_ERRORS = "sentry.hive.authorization.errors";
+  public static final String HIVE_SENTRY_MOCK_COMPILATION = "hive.sentry.mock.compilation";
+  public static final String HIVE_SENTRY_MOCK_ERROR = "hive.sentry.mock.error";
+  public static final String HIVE_SENTRY_PRIVILEGE_ERROR_MESSAGE = "No valid privileges";
 
   /**
    * Config setting definitions
@@ -132,11 +138,13 @@ public class HiveAuthzConf extends Configuration {
   private static final Logger LOG = LoggerFactory
       .getLogger(HiveAuthzConf.class);
   public static final String AUTHZ_SITE_FILE = "sentry-site.xml";
+  private final String hiveAuthzSiteFile;
 
   public HiveAuthzConf(URL hiveAuthzSiteURL) {
     super(false);
     addResource(hiveAuthzSiteURL);
     applySystemProperties();
+    this.hiveAuthzSiteFile = hiveAuthzSiteURL.toString();
   }
   /**
    * Apply system properties to this object if the property name is defined in ConfVars
@@ -182,4 +190,47 @@ public class HiveAuthzConf extends Configuration {
     }
     return retVal;
   }
+
+  public String getHiveAuthzSiteFile() {
+    return hiveAuthzSiteFile;
+  }
+
+  /**
+   * Extract the authz config file path from given hive conf and load the authz config
+   * @param hiveConf
+   * @return
+   * @throws IllegalArgumentException
+   */
+  public static HiveAuthzConf getAuthzConf(HiveConf hiveConf)
+    throws IllegalArgumentException {
+    boolean depreicatedConfigFile = false;
+
+    String hiveAuthzConf = hiveConf.get(HiveAuthzConf.HIVE_SENTRY_CONF_URL);
+    if (hiveAuthzConf == null
+        || (hiveAuthzConf = hiveAuthzConf.trim()).isEmpty()) {
+      hiveAuthzConf = hiveConf.get(HiveAuthzConf.HIVE_ACCESS_CONF_URL);
+      depreicatedConfigFile = true;
+    }
+
+    if (hiveAuthzConf == null
+        || (hiveAuthzConf = hiveAuthzConf.trim()).isEmpty()) {
+      throw new IllegalArgumentException("Configuration key "
+          + HiveAuthzConf.HIVE_SENTRY_CONF_URL + " value '" + hiveAuthzConf
+          + "' is invalid.");
+    }
+
+    try {
+      return new HiveAuthzConf(new URL(hiveAuthzConf));
+    } catch (MalformedURLException e) {
+      if (depreicatedConfigFile) {
+        throw new IllegalArgumentException("Configuration key "
+            + HiveAuthzConf.HIVE_ACCESS_CONF_URL
+            + " specifies a malformed URL '" + hiveAuthzConf + "'", e);
+      } else {
+        throw new IllegalArgumentException("Configuration key "
+            + HiveAuthzConf.HIVE_SENTRY_CONF_URL
+            + " specifies a malformed URL '" + hiveAuthzConf + "'", e);
+      }
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-core/sentry-core-common/pom.xml
----------------------------------------------------------------------
diff --git a/sentry-core/sentry-core-common/pom.xml b/sentry-core/sentry-core-common/pom.xml
index a14f129..d50963e 100644
--- a/sentry-core/sentry-core-common/pom.xml
+++ b/sentry-core/sentry-core-common/pom.xml
@@ -33,6 +33,10 @@ limitations under the License.
       <artifactId>guava</artifactId>
     </dependency>
     <dependency>
+     <groupId>org.apache.shiro</groupId>
+     <artifactId>shiro-core</artifactId>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/SentryConfigurationException.java
----------------------------------------------------------------------
diff --git a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/SentryConfigurationException.java b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/SentryConfigurationException.java
new file mode 100644
index 0000000..516b2da
--- /dev/null
+++ b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/SentryConfigurationException.java
@@ -0,0 +1,67 @@
+/*
+ * 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.core.common;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.shiro.config.ConfigurationException;
+
+public class SentryConfigurationException extends ConfigurationException {
+  private List<String> configErrors = new ArrayList<String>();
+  private List<String> configWarnings = new ArrayList<String>();
+
+  public boolean hasWarnings() {
+    return !configWarnings.isEmpty();
+  }
+
+  public boolean hasErrors() {
+    return !configErrors.isEmpty();
+  }
+
+  public SentryConfigurationException() {
+    super();
+  }
+
+  public SentryConfigurationException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public SentryConfigurationException(String message) {
+    super(message);
+  }
+
+  public SentryConfigurationException(Throwable cause) {
+    super(cause);
+  }
+
+  public List<String> getConfigErrors() {
+    return configErrors;
+  }
+
+  public void setConfigErrors(List<String> configErrors) {
+    this.configErrors = configErrors;
+  }
+
+  public List<String> getConfigWarnings() {
+    return configWarnings;
+  }
+
+  public void setConfigWarnings(List<String> configWarnings) {
+    this.configWarnings = configWarnings;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-policy/sentry-policy-common/src/main/java/org/apache/sentry/policy/common/PolicyEngine.java
----------------------------------------------------------------------
diff --git a/sentry-policy/sentry-policy-common/src/main/java/org/apache/sentry/policy/common/PolicyEngine.java b/sentry-policy/sentry-policy-common/src/main/java/org/apache/sentry/policy/common/PolicyEngine.java
index 693de1b..c08d082 100644
--- a/sentry-policy/sentry-policy-common/src/main/java/org/apache/sentry/policy/common/PolicyEngine.java
+++ b/sentry-policy/sentry-policy-common/src/main/java/org/apache/sentry/policy/common/PolicyEngine.java
@@ -20,7 +20,9 @@ package org.apache.sentry.policy.common;
 import java.util.List;
 
 import org.apache.sentry.core.common.Authorizable;
+import org.apache.sentry.core.common.SentryConfigurationException;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
 
 public interface PolicyEngine {
@@ -41,6 +43,15 @@ public interface PolicyEngine {
    * @param group name
    * @return non-null immutable set of permissions
    */
-  public ImmutableSetMultimap<String, String> getPermissions(List<? extends Authorizable> authorizables, List<String> groups);
+  public ImmutableSetMultimap<String, String> getPermissions(
+      List<? extends Authorizable> authorizables, List<String> groups)
+      throws SentryConfigurationException;
 
+  public ImmutableSet<String> listPermissions(String groupName)
+    throws SentryConfigurationException;
+
+  public ImmutableSet<String> listPermissions(List<String> groupName)
+    throws SentryConfigurationException;
+
+  public void validatePolicy(boolean strictValidation) throws SentryConfigurationException;
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-policy/sentry-policy-db/src/main/java/org/apache/sentry/policy/db/SimpleDBPolicyEngine.java
----------------------------------------------------------------------
diff --git a/sentry-policy/sentry-policy-db/src/main/java/org/apache/sentry/policy/db/SimpleDBPolicyEngine.java b/sentry-policy/sentry-policy-db/src/main/java/org/apache/sentry/policy/db/SimpleDBPolicyEngine.java
index 1d72f87..1d01b47 100644
--- a/sentry-policy/sentry-policy-db/src/main/java/org/apache/sentry/policy/db/SimpleDBPolicyEngine.java
+++ b/sentry-policy/sentry-policy-db/src/main/java/org/apache/sentry/policy/db/SimpleDBPolicyEngine.java
@@ -19,11 +19,14 @@ package org.apache.sentry.policy.db;
 import javax.annotation.Nullable;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map.Entry;
+
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
 import org.apache.sentry.core.common.Authorizable;
+import org.apache.sentry.core.common.SentryConfigurationException;
 import org.apache.sentry.core.model.db.AccessURI;
 import org.apache.sentry.core.model.db.Database;
 import org.apache.sentry.policy.common.PermissionFactory;
@@ -36,7 +39,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
-
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
@@ -50,13 +52,15 @@ public class SimpleDBPolicyEngine implements PolicyEngine {
   public final static String ACCESS_ALLOW_URI_PER_DB_POLICYFILE = "sentry.allow.uri.db.policyfile";
 
   private ProviderBackend providerBackend;
+  private String serverName;
+  private List<? extends RoleValidator> validators;
 
   public SimpleDBPolicyEngine(String serverName, ProviderBackend providerBackend) {
-    List<? extends RoleValidator> validators =
-      Lists.newArrayList(new ServersAllIsInvalid(), new DatabaseMustMatch(),
-        new DatabaseRequiredInRole(), new ServerNameMustMatch(serverName));
+    validators = Lists.newArrayList(new ServersAllIsInvalid(), new DatabaseMustMatch(),
+          new DatabaseRequiredInRole(), new ServerNameMustMatch(serverName));
     this.providerBackend = providerBackend;
     this.providerBackend.process(validators);
+    this.serverName = serverName;
   }
 
   /**
@@ -71,7 +75,9 @@ public class SimpleDBPolicyEngine implements PolicyEngine {
    * {@inheritDoc}
    */
   @Override
-  public ImmutableSetMultimap<String, String> getPermissions(List<? extends Authorizable> authorizables, List<String> groups) {
+  public ImmutableSetMultimap<String, String> getPermissions(
+      List<? extends Authorizable> authorizables, List<String> groups)
+      throws SentryConfigurationException {
     String database = null;
     Boolean isURI = false;
     for(Authorizable authorizable : authorizables) {
@@ -139,4 +145,25 @@ public class SimpleDBPolicyEngine implements PolicyEngine {
     }
     return result;
   }
+
+  @Override
+  public void validatePolicy(boolean strictValidation) throws SentryConfigurationException {
+    this.providerBackend.validatePolicy(validators, strictValidation);
+  }
+
+  @Override
+  public ImmutableSet<String> listPermissions(String groupName) throws SentryConfigurationException {
+    return getDBRoles(Database.ALL.getName(), groupName, true, providerBackend.getRoles());
+  }
+
+  @Override
+  public ImmutableSet<String> listPermissions(List<String> groupNames)
+      throws SentryConfigurationException {
+    ImmutableSet.Builder<String> resultBuilder = ImmutableSet.builder();
+    for (String groupName : groupNames) {
+      resultBuilder.addAll(listPermissions(groupName));
+    }
+    return resultBuilder.build();
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-policy/sentry-policy-search/src/main/java/org/apache/sentry/policy/search/SimpleSearchPolicyEngine.java
----------------------------------------------------------------------
diff --git a/sentry-policy/sentry-policy-search/src/main/java/org/apache/sentry/policy/search/SimpleSearchPolicyEngine.java b/sentry-policy/sentry-policy-search/src/main/java/org/apache/sentry/policy/search/SimpleSearchPolicyEngine.java
index 21711ef..51ab35d 100644
--- a/sentry-policy/sentry-policy-search/src/main/java/org/apache/sentry/policy/search/SimpleSearchPolicyEngine.java
+++ b/sentry-policy/sentry-policy-search/src/main/java/org/apache/sentry/policy/search/SimpleSearchPolicyEngine.java
@@ -21,8 +21,10 @@ import javax.annotation.Nullable;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map.Entry;
+
 import org.apache.shiro.config.ConfigurationException;
 import org.apache.sentry.core.common.Authorizable;
+import org.apache.sentry.core.common.SentryConfigurationException;
 import org.apache.sentry.policy.common.PermissionFactory;
 import org.apache.sentry.policy.common.PolicyEngine;
 import org.apache.sentry.policy.common.RoleValidator;
@@ -33,7 +35,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
-
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
@@ -111,4 +112,23 @@ public class SimpleSearchPolicyEngine implements PolicyEngine {
     }
     return result;
   }
+
+  @Override
+  public ImmutableSet<String> listPermissions(String groupName)
+      throws SentryConfigurationException {
+    // TODO: not supported yet
+    throw new SentryConfigurationException("Not implemented yet");
+  }
+
+  @Override
+  public ImmutableSet<String> listPermissions(List<String> groupName)
+      throws SentryConfigurationException {
+    throw new SentryConfigurationException("Not implemented yet");
+  }
+
+  @Override
+  public void validatePolicy(boolean strictValidation)
+      throws SentryConfigurationException {
+    throw new SentryConfigurationException("Not implemented yet");
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/AuthorizationProvider.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/AuthorizationProvider.java b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/AuthorizationProvider.java
index 1244755..4887678 100644
--- a/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/AuthorizationProvider.java
+++ b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/AuthorizationProvider.java
@@ -21,6 +21,7 @@ import java.util.Set;
 
 import org.apache.sentry.core.common.Action;
 import org.apache.sentry.core.common.Authorizable;
+import org.apache.sentry.core.common.SentryConfigurationException;
 import org.apache.sentry.core.common.Subject;
 
 public interface AuthorizationProvider {
@@ -44,4 +45,33 @@ public interface AuthorizationProvider {
    * @return GroupMappingService used by the AuthorizationProvider
    */
   public GroupMappingService getGroupMapping();
+
+  /***
+   * Validate the policy file format for syntax and semantic errors
+   * @param strictValidation
+   * @throws SentryConfigurationException
+   */
+  public void validateResource(boolean strictValidation) throws SentryConfigurationException;
+
+  /***
+   * Returns the list privileges for the given subject
+   * @param subject
+   * @return
+   * @throws SentryConfigurationException
+   */
+  public Set<String> listPermissionsForSubject(Subject subject) throws SentryConfigurationException;
+
+  /**
+   * Returns the list privileges for the given group
+   * @param groupName
+   * @return
+   * @throws SentryConfigurationException
+   */
+  public Set<String> listPermissionsForGroup(String groupName) throws SentryConfigurationException;
+
+  /***
+   * Returns the list of missing privileges of the last access request
+   * @return
+   */
+  public List<String> getLastFailedPermissions();
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/NoAuthorizationProvider.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/NoAuthorizationProvider.java b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/NoAuthorizationProvider.java
index f48eafe..8f18926 100644
--- a/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/NoAuthorizationProvider.java
+++ b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/NoAuthorizationProvider.java
@@ -16,11 +16,14 @@
  */
 package org.apache.sentry.provider.common;
 
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import org.apache.sentry.core.common.Action;
 import org.apache.sentry.core.common.Authorizable;
+import org.apache.sentry.core.common.SentryConfigurationException;
 import org.apache.sentry.core.common.Subject;
 
 public class NoAuthorizationProvider implements AuthorizationProvider {
@@ -36,4 +39,27 @@ public class NoAuthorizationProvider implements AuthorizationProvider {
   public GroupMappingService getGroupMapping() {
     return noGroupMappingService;
   }
+
+  @Override
+  public void validateResource(boolean strictValidation) throws SentryConfigurationException {
+    return;
+  }
+
+  @Override
+  public Set<String> listPermissionsForSubject(Subject subject)
+      throws SentryConfigurationException {
+    return new HashSet<String>();
+  }
+
+  @Override
+  public Set<String> listPermissionsForGroup(String groupName)
+      throws SentryConfigurationException {
+    return new HashSet<String>();
+  }
+
+  @Override
+  public List<String> getLastFailedPermissions() {
+    return new ArrayList<String>();
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/ProviderBackend.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/ProviderBackend.java b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/ProviderBackend.java
index 415a509..327a3a5 100644
--- a/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/ProviderBackend.java
+++ b/sentry-provider/sentry-provider-common/src/main/java/org/apache/sentry/provider/common/ProviderBackend.java
@@ -17,8 +17,12 @@
 package org.apache.sentry.provider.common;
 
 import javax.annotation.Nullable;
+
 import java.util.List;
+
+import org.apache.sentry.core.common.SentryConfigurationException;
 import org.apache.sentry.policy.common.RoleValidator;
+
 import com.google.common.collect.ImmutableSet;
 
 /**
@@ -36,4 +40,7 @@ public interface ProviderBackend {
    * least once prior.
    */
   public Roles getRoles();
+
+  public void validatePolicy(List<? extends RoleValidator> validators, boolean strictValidation)
+      throws SentryConfigurationException;
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/ResourceAuthorizationProvider.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/ResourceAuthorizationProvider.java b/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/ResourceAuthorizationProvider.java
index 205d012..0743604 100644
--- a/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/ResourceAuthorizationProvider.java
+++ b/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/ResourceAuthorizationProvider.java
@@ -21,11 +21,13 @@ import static org.apache.sentry.provider.file.PolicyFileConstants.KV_JOINER;
 import static org.apache.sentry.provider.file.PolicyFileConstants.PRIVILEGE_NAME;
 
 import java.util.ArrayList;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Set;
 
 import org.apache.sentry.core.common.Action;
 import org.apache.sentry.core.common.Authorizable;
+import org.apache.sentry.core.common.SentryConfigurationException;
 import org.apache.sentry.core.common.Subject;
 import org.apache.sentry.policy.common.PermissionFactory;
 import org.apache.sentry.policy.common.PolicyEngine;
@@ -46,6 +48,7 @@ public abstract class ResourceAuthorizationProvider implements AuthorizationProv
   private final GroupMappingService groupService;
   private final PolicyEngine policy;
   private final PermissionFactory permissionFactory;
+  private final List<String> lastFailedPermissions = new ArrayList<String>();
 
   public ResourceAuthorizationProvider(PolicyEngine policy,
       GroupMappingService groupService) {
@@ -80,16 +83,16 @@ public abstract class ResourceAuthorizationProvider implements AuthorizationProv
 
   private boolean doHasAccess(Subject subject,
       List<? extends Authorizable> authorizables, Set<? extends Action> actions) {
-    List<String> groups = groupService.getGroups(subject.getName());
+    List<String> groups =  getGroups(subject);
     List<String> hierarchy = new ArrayList<String>();
     for (Authorizable authorizable : authorizables) {
       hierarchy.add(KV_JOINER.join(authorizable.getTypeName(), authorizable.getName()));
     }
     Iterable<Permission> permissions = getPermissions(authorizables, groups);
-    for (Action action : actions) {
-      String requestPermission = AUTHORIZABLE_JOINER.join(hierarchy);
-      requestPermission = AUTHORIZABLE_JOINER.join(requestPermission,
-          KV_JOINER.join(PRIVILEGE_NAME, action.getValue()));
+    List<String> requestPermissions = buildPermissions(authorizables, actions);
+    lastFailedPermissions.clear();
+
+    for (String requestPermission : requestPermissions) {
       for (Permission permission : permissions) {
         /*
          * Does the permission granted in the policy file imply the requested action?
@@ -104,6 +107,7 @@ public abstract class ResourceAuthorizationProvider implements AuthorizationProv
         }
       }
     }
+    lastFailedPermissions.addAll(requestPermissions);
     return false;
   }
 
@@ -121,4 +125,47 @@ public abstract class ResourceAuthorizationProvider implements AuthorizationProv
   public GroupMappingService getGroupMapping() {
     return groupService;
   }
+
+  private List<String> getGroups(Subject subject) {
+    return groupService.getGroups(subject.getName());
+  }
+
+  @Override
+  public void validateResource(boolean strictValidation) throws SentryConfigurationException {
+    policy.validatePolicy(strictValidation);
+  }
+
+  @Override
+  public Set<String> listPermissionsForSubject(Subject subject) throws SentryConfigurationException {
+    return policy.listPermissions(getGroups(subject));
+  }
+
+  @Override
+  public Set<String> listPermissionsForGroup(String groupName) throws SentryConfigurationException {
+    return policy.listPermissions(groupName);
+  }
+
+  @Override
+  public List<String> getLastFailedPermissions() {
+    return lastFailedPermissions;
+  }
+
+  private List<String> buildPermissions(List<? extends Authorizable> authorizables,
+      Set<? extends Action> actions) {
+    List<String> hierarchy = new ArrayList<String>();
+    List<String> requestedPermissions = new ArrayList<String>();
+
+    for (Authorizable authorizable : authorizables) {
+      hierarchy.add(KV_JOINER.join(authorizable.getTypeName(), authorizable.getName()));
+    }
+
+    for (Action action : actions) {
+      String requestPermission = AUTHORIZABLE_JOINER.join(hierarchy);
+      requestPermission = AUTHORIZABLE_JOINER.join(requestPermission,
+          KV_JOINER.join(PRIVILEGE_NAME, action.getValue()));
+      requestedPermissions.add(requestPermission);
+    }
+    return requestedPermissions;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/SimpleFileProviderBackend.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/SimpleFileProviderBackend.java b/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/SimpleFileProviderBackend.java
index f432915..9eabb53 100644
--- a/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/SimpleFileProviderBackend.java
+++ b/sentry-provider/sentry-provider-file/src/main/java/org/apache/sentry/provider/file/SimpleFileProviderBackend.java
@@ -25,6 +25,7 @@ import static org.apache.sentry.provider.file.PolicyFileConstants.USERS;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -36,6 +37,7 @@ import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.sentry.core.common.Authorizable;
+import org.apache.sentry.core.common.SentryConfigurationException;
 import org.apache.sentry.policy.common.RoleValidator;
 import org.apache.sentry.provider.common.ProviderBackend;
 import org.apache.sentry.provider.common.Roles;
@@ -70,6 +72,8 @@ public class SimpleFileProviderBackend implements ProviderBackend {
   private Roles rolesStorage;
   private final Configuration conf;
   private boolean processed;
+  private final List<String> configErrors = new ArrayList<String>();
+  private final List<String> configWarnings = new ArrayList<String>();
 
   public SimpleFileProviderBackend(String resourcePath) throws IOException {
     this(new Configuration(), resourcePath);
@@ -92,11 +96,26 @@ public class SimpleFileProviderBackend implements ProviderBackend {
    * {@inheritDoc}
    */
   public void process(List<? extends RoleValidator> validators) {
+    configErrors.clear();
+    perDbResources.clear();
+    Ini ini;
+
     LOGGER.info("Parsing " + resourcePath);
     Roles roles = new Roles();
     try {
       perDbResources.clear();
-      Ini ini = PolicyFiles.loadFromPath(fileSystem, resourcePath);
+      try {
+        ini = PolicyFiles.loadFromPath(fileSystem, resourcePath);
+      } catch (IOException e) {
+        configErrors.add("Failed to read policy file " + resourcePath +
+          " Error: " + e.getMessage());
+        throw new SentryConfigurationException("Error loading policy file " + resourcePath, e);
+      } catch (IllegalArgumentException e) {
+        configErrors.add("Failed to read policy file " + resourcePath +
+          " Error: " + e.getMessage());
+        throw new SentryConfigurationException("Error loading policy file " + resourcePath, e);
+      }
+
       if(LOGGER.isDebugEnabled()) {
         for(String sectionName : ini.getSectionNames()) {
           LOGGER.debug("Section: " + sectionName);
@@ -109,7 +128,7 @@ public class SimpleFileProviderBackend implements ProviderBackend {
       }
       ImmutableSetMultimap<String, String> globalRoles;
       Map<String, ImmutableSetMultimap<String, String>> perDatabaseRoles = Maps.newHashMap();
-      globalRoles = parseIni(null, ini, validators);
+      globalRoles = parseIni(null, ini, validators, resourcePath);
       Ini.Section filesSection = ini.getSection(DATABASES);
       if(filesSection == null) {
         LOGGER.info("Section " + DATABASES + " needs no further processing");
@@ -124,21 +143,27 @@ public class SimpleFileProviderBackend implements ProviderBackend {
             LOGGER.info("Parsing " + perDbPolicy);
             Ini perDbIni = PolicyFiles.loadFromPath(perDbPolicy.getFileSystem(conf), perDbPolicy);
             if(perDbIni.containsKey(USERS)) {
+              configErrors.add("Per-db policy file cannot contain " + USERS + " section in " +  perDbPolicy);
               throw new ConfigurationException("Per-db policy files cannot contain " + USERS + " section");
             }
             if(perDbIni.containsKey(DATABASES)) {
+              configErrors.add("Per-db policy files cannot contain " + DATABASES
+                  + " section in " + perDbPolicy);
               throw new ConfigurationException("Per-db policy files cannot contain " + DATABASES + " section");
             }
-            ImmutableSetMultimap<String, String> currentDbRoles = parseIni(database, perDbIni, validators);
+            ImmutableSetMultimap<String, String> currentDbRoles = parseIni(database, perDbIni, validators, perDbPolicy);
             perDatabaseRoles.put(database, currentDbRoles);
             perDbResources.add(perDbPolicy);
           } catch (Exception e) {
+            configErrors.add("Failed to read per-DB policy file " + perDbPolicy +
+               " Error: " + e.getMessage());
             LOGGER.error("Error processing key " + entry.getKey() + ", skipping " + entry.getValue(), e);
           }
         }
       }
       roles = new Roles(globalRoles, ImmutableMap.copyOf(perDatabaseRoles));
     } catch (Exception e) {
+      configErrors.add("Error processing file " + resourcePath + e.getMessage());
       LOGGER.error("Error processing file, ignoring " + resourcePath, e);
     }
     rolesStorage = roles;
@@ -167,26 +192,32 @@ public class SimpleFileProviderBackend implements ProviderBackend {
     return result;
   }
 
-  private ImmutableSetMultimap<String, String> parseIni(String database, Ini ini, List<? extends RoleValidator> validators) {
+  private ImmutableSetMultimap<String, String> parseIni(String database, Ini ini, List<? extends RoleValidator> validators,
+      Path policyPath) {
     Ini.Section privilegesSection = ini.getSection(ROLES);
     boolean invalidConfiguration = false;
     if (privilegesSection == null) {
-      LOGGER.warn("Section {} empty for {}", ROLES, resourcePath);
+      String errMsg = String.format("Section %s empty for %s", ROLES, policyPath);
+      LOGGER.warn(errMsg);
+      configErrors.add(errMsg);
       invalidConfiguration = true;
     }
     Ini.Section groupsSection = ini.getSection(GROUPS);
     if (groupsSection == null) {
-      LOGGER.warn("Section {} empty for {}", GROUPS, resourcePath);
+      String warnMsg = String.format("Section %s empty for %s", GROUPS, policyPath);
+      LOGGER.warn(warnMsg);
+      configErrors.add(warnMsg);
       invalidConfiguration = true;
     }
     if (!invalidConfiguration) {
-      return parsePermissions(database, privilegesSection, groupsSection, validators);
+      return parsePermissions(database, privilegesSection, groupsSection, validators, policyPath);
     }
     return ImmutableSetMultimap.of();
   }
 
   private ImmutableSetMultimap<String, String> parsePermissions(@Nullable String database,
-      Ini.Section rolesSection, Ini.Section groupsSection, List<? extends RoleValidator> validators) {
+      Ini.Section rolesSection, Ini.Section groupsSection, List<? extends RoleValidator> validators,
+      Path policyPath) {
     ImmutableSetMultimap.Builder<String, String> resultBuilder = ImmutableSetMultimap.builder();
     Multimap<String, String> roleNameToPrivilegeMap = HashMultimap
         .create();
@@ -195,16 +226,21 @@ public class SimpleFileProviderBackend implements ProviderBackend {
       String roleValue = Strings.nullToEmpty(entry.getValue()).trim();
       boolean invalidConfiguration = false;
       if (roleName.isEmpty()) {
-        LOGGER.warn("Empty role name encountered in {}", resourcePath);
+        String errMsg = String.format("Empty role name encountered in %s", policyPath);
+        LOGGER.warn(errMsg);
+        configErrors.add(errMsg);
         invalidConfiguration = true;
       }
       if (roleValue.isEmpty()) {
-        LOGGER.warn("Empty role value encountered in {}", resourcePath);
+        String errMsg = String.format("Empty role value encountered in %s", policyPath);
+        LOGGER.warn(errMsg);
+        configErrors.add(errMsg);
         invalidConfiguration = true;
       }
       if (roleNameToPrivilegeMap.containsKey(roleName)) {
-        LOGGER.warn("Role {} defined twice in {}", roleName,
-            resourcePath);
+        String warnMsg = String.format("Role %s defined twice in %s", roleName, policyPath);
+        LOGGER.warn(warnMsg);
+        configWarnings.add(warnMsg);
       }
       Set<String> roles = PermissionUtils
           .toPermissionStrings(roleValue);
@@ -227,8 +263,10 @@ public class SimpleFileProviderBackend implements ProviderBackend {
           resolvedGroupPrivileges.addAll(roleNameToPrivilegeMap
               .get(roleName));
         } else {
-          LOGGER.warn("Role {} for group {} does not exist in privileges section in {}",
-              new Object[] { roleName, groupName, resourcePath });
+          String warnMsg = String.format("Role %s for group %s does not exist in privileges section in %s",
+                  roleName, groupName, policyPath);
+          LOGGER.warn(warnMsg);
+          configWarnings.add(warnMsg);
         }
       }
       resultBuilder.putAll(groupName, resolvedGroupPrivileges);
@@ -244,4 +282,17 @@ public class SimpleFileProviderBackend implements ProviderBackend {
 
     return rolesStorage;
   }
+
+  @Override
+  public void validatePolicy(List<? extends RoleValidator> validators, boolean strictValidation)
+      throws SentryConfigurationException {
+    if ((strictValidation && !configWarnings.isEmpty()) || !configErrors.isEmpty()) {
+      configErrors.add("Failed to process global policy file " + resourcePath);
+      SentryConfigurationException e = new SentryConfigurationException("");
+      e.setConfigErrors(configErrors);
+      e.setConfigWarnings(configWarnings);
+      throw e;
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-provider/sentry-provider-file/src/test/java/org/apache/sentry/provider/file/TestGetGroupMapping.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-file/src/test/java/org/apache/sentry/provider/file/TestGetGroupMapping.java b/sentry-provider/sentry-provider-file/src/test/java/org/apache/sentry/provider/file/TestGetGroupMapping.java
index a4d4bb3..a50bd24 100644
--- a/sentry-provider/sentry-provider-file/src/test/java/org/apache/sentry/provider/file/TestGetGroupMapping.java
+++ b/sentry-provider/sentry-provider-file/src/test/java/org/apache/sentry/provider/file/TestGetGroupMapping.java
@@ -18,12 +18,18 @@ package org.apache.sentry.provider.file;
 
 import java.util.Arrays;
 import java.util.List;
+
 import org.apache.sentry.core.common.Authorizable;
+import org.apache.sentry.core.common.SentryConfigurationException;
 import org.apache.sentry.policy.common.PermissionFactory;
 import org.apache.sentry.policy.common.PolicyEngine;
 import org.apache.sentry.provider.common.GroupMappingService;
+
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
+
 import org.junit.Test;
+
 import static org.junit.Assert.assertSame;
 
 public class TestGetGroupMapping {
@@ -45,6 +51,21 @@ public class TestGetGroupMapping {
       public PermissionFactory getPermissionFactory() { return null; }
 
       public ImmutableSetMultimap<String, String> getPermissions(List<? extends Authorizable> authorizables, List<String> groups) { return null; }
+
+      public ImmutableSet<String> listPermissions(String groupName)
+          throws SentryConfigurationException {
+        return null;
+      }
+
+      public ImmutableSet<String> listPermissions(List<String> groupName)
+          throws SentryConfigurationException {
+        return null;
+      }
+
+      public void validatePolicy(boolean strictValidation)
+          throws SentryConfigurationException {
+        return;
+      }
     };
 
     TestResourceAuthorizationProvider authProvider =

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/Context.java
----------------------------------------------------------------------
diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/Context.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/Context.java
index 66cd2d1..2f83678 100644
--- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/Context.java
+++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/Context.java
@@ -209,4 +209,8 @@ public class Context {
   public String getProperty(String propName) {
     return hiveServer.getProperty(propName);
   }
-}
\ No newline at end of file
+
+  public String getConnectionURL() {
+    return hiveServer.getURL();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/a4819f5b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestConfigTool.java
----------------------------------------------------------------------
diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestConfigTool.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestConfigTool.java
new file mode 100644
index 0000000..6968cc0
--- /dev/null
+++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestConfigTool.java
@@ -0,0 +1,304 @@
+/*
+ * 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.tests.e2e.hive;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.Assert;
+
+import org.apache.sentry.binding.hive.authz.SentryConfigTool;
+import org.apache.sentry.binding.hive.conf.HiveAuthzConf;
+import org.apache.sentry.core.common.SentryConfigurationException;
+import org.apache.sentry.core.common.Subject;
+import org.apache.sentry.provider.file.PolicyFile;
+
+import com.google.common.io.Resources;
+
+public class TestConfigTool extends AbstractTestWithStaticConfiguration {
+  private static final String DB2_POLICY_FILE = "db2-policy-file.ini";
+  private static String prefix;
+
+  private PolicyFile policyFile;
+  private SentryConfigTool configTool;
+
+  @Before
+  public void setup() throws Exception {
+    context = createContext();
+    policyFile = PolicyFile.setAdminOnServer1(ADMINGROUP);
+    configTool = new SentryConfigTool();
+    String hiveServer2 = System.getProperty("sentry.e2etest.hiveServer2Type",
+        "InternalHiveServer2");
+    String policyOnHDFS = System.getProperty(
+        "sentry.e2etest.hive.policyOnHDFS", "true");
+    if (policyOnHDFS.trim().equalsIgnoreCase("true")
+        && (hiveServer2.equals("UnmanagedHiveServer2"))) {
+      String policyLocation = System.getProperty(
+          "sentry.e2etest.hive.policy.location", "/user/hive/sentry");
+      prefix = "hdfs://" + policyLocation + "/";
+    } else {
+      prefix = "file://" + context.getPolicyFile().getParent() + "/";
+    }
+
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    if (context != null) {
+      context.close();
+    }
+  }
+
+  /**
+   * Verify errors/warnings from malformed policy file
+   * @throws Exception
+   */
+  @Test
+  public void testInvalidPolicy() throws Exception {
+    // policy file, missing insert_tab2 and select_tab3 role definition
+    policyFile
+        .addRolesToGroup(USERGROUP1, "select_tab1", "insert_tab2")
+        .addRolesToGroup(USERGROUP2, "select_tab3")
+        .addPermissionsToRole("select_tab1",
+            "server=server1->db=db1->table=tab1->action=select")
+        .setUserGroupMapping(StaticUserGroup.getStaticMapping());
+    policyFile.write(context.getPolicyFile());
+
+    configTool.setPolicyFile(context.getPolicyFile().getPath());
+    configTool.setupConfig();
+    try {
+      configTool.getSentryProvider().validateResource(true);
+      fail("Policy validation should fail for malformed policy");
+    } catch (SentryConfigurationException e) {
+      assertTrue(e
+          .getConfigWarnings()
+          .get(0)
+          .contains(
+              "Role select_tab3 for group " + USERGROUP2 + " does not exist"));
+      assertTrue(e
+          .getConfigWarnings()
+          .get(1)
+          .contains(
+              "Role insert_tab2 for group " + USERGROUP1 + " does not exist"));
+    }
+  }
+
+  /**
+   * Verify errors/warnings from malformed policy file with per-DB policy
+   * @throws Exception
+   */
+  @Test
+  public void testInvalidPerDbPolicy() throws Exception {
+    PolicyFile db2PolicyFile = new PolicyFile();
+    File db2PolicyFileHandle = new File(context.getPolicyFile().getParent(),
+        DB2_POLICY_FILE);
+    // invalid db2 policy file with missing roles
+    db2PolicyFile
+        .addRolesToGroup(USERGROUP2, "select_tbl2", "insert_db2_tab2")
+        .addPermissionsToRole("select_tbl2",
+            "server=server1->db=db2->table=tbl2->action=select")
+        .write(db2PolicyFileHandle);
+
+    policyFile
+        .addRolesToGroup(USERGROUP1, "select_tbl1")
+        .addRolesToGroup(USERGROUP2, "select_tbl3")
+        .addPermissionsToRole("select_tbl1",
+            "server=server1->db=db1->table=tbl1->action=select")
+        .addDatabase("db2", prefix + db2PolicyFileHandle.getName())
+        .setUserGroupMapping(StaticUserGroup.getStaticMapping())
+        .write(context.getPolicyFile());
+
+    configTool.setPolicyFile(context.getPolicyFile().getPath());
+    configTool.setupConfig();
+    try {
+      configTool.getSentryProvider().validateResource(true);
+      fail("Policy validation should fail for malformed policy");
+    } catch (SentryConfigurationException e) {
+      assertTrue(e
+          .getConfigWarnings()
+          .get(0)
+          .contains(
+              "Role select_tbl3 for group " + USERGROUP2 + " does not exist"));
+      assertTrue(e.getConfigWarnings().get(0)
+          .contains(context.getPolicyFile().getName()));
+      assertTrue(e
+          .getConfigWarnings()
+          .get(1)
+          .contains(
+              "Role insert_db2_tab2 for group " + USERGROUP2
+                  + " does not exist"));
+      assertTrue(e.getConfigWarnings().get(1)
+          .contains(db2PolicyFileHandle.getName()));
+    }
+  }
+
+  /**
+   * Validate user permissions listing
+   * @throws Exception
+   */
+  @Test
+  public void testUserPermissions() throws Exception {
+    policyFile
+        .addRolesToGroup(USERGROUP1, "select_tab1", "insert_tab2")
+        .addRolesToGroup(USERGROUP2, "select_tab3")
+        .addPermissionsToRole("select_tab1",
+            "server=server1->db=db1->table=tab1->action=select")
+        .addPermissionsToRole("insert_tab2",
+            "server=server1->db=db1->table=tab2->action=insert")
+        .addPermissionsToRole("select_tab3",
+            "server=server1->db=db1->table=tab3->action=select")
+        .setUserGroupMapping(StaticUserGroup.getStaticMapping());
+    policyFile.write(context.getPolicyFile());
+
+    configTool.setPolicyFile(context.getPolicyFile().getPath());
+    configTool.setupConfig();
+    configTool.validatePolicy();
+
+    Set<String> permList = configTool.getSentryProvider()
+        .listPermissionsForSubject(new Subject(USER1_1));
+    assertTrue(permList
+        .contains("server=server1->db=db1->table=tab1->action=select"));
+    assertTrue(permList
+        .contains("server=server1->db=db1->table=tab2->action=insert"));
+
+    permList = configTool.getSentryProvider().listPermissionsForSubject(
+        new Subject(USER2_1));
+    assertTrue(permList
+        .contains("server=server1->db=db1->table=tab3->action=select"));
+
+    permList = configTool.getSentryProvider().listPermissionsForSubject(
+        new Subject(ADMIN1));
+    assertTrue(permList.contains("server=server1"));
+  }
+
+  /***
+   * Verify the mock compilation config setting forces query to abort
+   * @throws Exception
+   */
+  @Test
+  public void testMockCompilation() throws Exception {
+    policyFile.setUserGroupMapping(StaticUserGroup.getStaticMapping());
+    policyFile.write(context.getPolicyFile());
+    // setup db objects needed by the test
+    Connection connection = context.createConnection(ADMIN1);
+    Statement statement = context.createStatement(connection);
+
+    statement.execute("DROP TABLE IF EXISTS tab1");
+    statement.execute("CREATE TABLE tab1(B INT, A STRING) "
+        + " row format delimited fields terminated by '|'  stored as textfile");
+    statement.execute("SELECT * FROM tab1");
+
+    statement.execute("SET " + HiveAuthzConf.HIVE_SENTRY_MOCK_COMPILATION
+        + "=true");
+    try {
+      statement.execute("SELECT * FROM tab1");
+      fail("Query should fail with mock error config enabled");
+    } catch (SQLException e) {
+      assertTrue(e.getMessage().contains(HiveAuthzConf.HIVE_SENTRY_MOCK_ERROR));
+    }
+    statement.close();
+
+  }
+
+  /**
+   * verify missing permissions for query using remote query validation
+   * @throws Exception
+   */
+  @Test
+  public void testQueryPermissions() throws Exception {
+    policyFile
+        .addRolesToGroup(USERGROUP1, "select_tab1", "insert_tab2")
+        .addRolesToGroup(USERGROUP2, "select_tab3")
+        .addPermissionsToRole("select_tab1",
+            "server=server1->db=default->table=tab1->action=select")
+        .addPermissionsToRole("insert_tab2",
+            "server=server1->db=default->table=tab2->action=insert")
+        .addPermissionsToRole("select_tab3",
+            "server=server1->db=default->table=tab3->action=select")
+        .setUserGroupMapping(StaticUserGroup.getStaticMapping());
+    policyFile.write(context.getPolicyFile());
+
+    // setup db objects needed by the test
+    Connection connection = context.createConnection(ADMIN1);
+    Statement statement = context.createStatement(connection);
+
+    statement.execute("DROP TABLE IF EXISTS tab1");
+    statement.execute("DROP TABLE IF EXISTS tab2");
+    statement.execute("DROP TABLE IF EXISTS tab3");
+    statement.execute("CREATE TABLE tab1(B INT, A STRING) "
+        + " row format delimited fields terminated by '|'  stored as textfile");
+    statement.execute("CREATE TABLE tab2(B INT, A STRING) "
+        + " row format delimited fields terminated by '|'  stored as textfile");
+    statement.execute("CREATE TABLE tab3(B INT, A STRING) "
+        + " row format delimited fields terminated by '|'  stored as textfile");
+    statement.close();
+    connection.close();
+
+    configTool.setPolicyFile(context.getPolicyFile().getPath());
+    configTool.setJdbcURL(context.getConnectionURL());
+    configTool.setUser(USER1_1);
+    configTool.setupConfig();
+    ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+
+    // user1_1 can query table1
+    configTool.setUser(USER1_1);
+    configTool.verifyRemoteQuery("SELECT COUNT(*) FROM tab1");
+
+    // user1_1 can't select from tab3
+    try {
+      System.setOut(new PrintStream(errBuffer));
+      configTool.setUser(USER1_1);
+      configTool.verifyRemoteQuery("SELECT COUNT(*) FROM tab3");
+      fail("Query should have failed with insufficient perms");
+    } catch (SQLException e) {
+      assertTrue(errBuffer.toString().contains(
+          "Server=server1->Db=default->Table=tab3->action=select"));
+      errBuffer.flush();
+    }
+
+    // user2_1 can select from tab3, but can't insert into tab2
+    try {
+      configTool.setUser(USER2_1);
+      configTool
+          .verifyRemoteQuery("INSERT OVERWRITE TABLE tab2 SELECT * FROM tab3");
+      fail("Query should have failed with insufficient perms");
+    } catch (SQLException e) {
+      assertTrue(errBuffer.toString().contains(
+          "Server=server1->Db=default->Table=tab2->action=insert"));
+    }
+
+  }
+}


Mime
View raw message