Author: brock
Date: Fri Nov 21 21:57:23 2014
New Revision: 1641007

URL: http://svn.apache.org/r1641007
Log:
HIVE-8945 - Allow user to read encrypted read-only tables only if the scratch 
directory is encrypted (Sergio Pena via Brock)

Modified:
    
hive/branches/HIVE-8065/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java

Modified: 
hive/branches/HIVE-8065/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java
URL: 
http://svn.apache.org/viewvc/hive/branches/HIVE-8065/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java?rev=1641007&r1=1641006&r2=1641007&view=diff
==============================================================================
--- 
hive/branches/HIVE-8065/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java
 (original)
+++ 
hive/branches/HIVE-8065/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java
 Fri Nov 21 21:57:23 2014
@@ -1815,13 +1815,9 @@ public class SemanticAnalyzer extends Ba
             } else {
               // This is the only place where isQuery is set to true; it 
defaults to false.
               qb.setIsQuery(true);
-              Path table_path = getStrongestEncryptedTablePath(qb);
-              if (table_path == null) {
-                fname = ctx.getMRTmpPath().toString();
-              } else {
-                fname = ctx.getMRTmpPath(table_path.toUri()).toString();
-              }
-              ctx.setResDir(new Path(fname));
+              Path stagingPath = getStagingDirectoryPathname(qb);
+              fname = stagingPath.toString();
+              ctx.setResDir(stagingPath);
             }
           }
           qb.getMetaData().setDestForAlias(name, fname,
@@ -1878,6 +1874,81 @@ public class SemanticAnalyzer extends Ba
   }
 
   /**
+   * Checks if a given path is encrypted (valid only for HDFS files)
+   * @param path The path to check for encryption
+   * @return True if the path is encrypted; False if it is not encrypted
+   * @throws HiveException If an error occurs while checking for encryption
+   */
+  private boolean isPathEncrypted(Path path) throws HiveException {
+    HadoopShims.HdfsEncryptionShim hdfsEncryptionShim;
+
+    hdfsEncryptionShim = SessionState.get().getHdfsEncryptionShim();
+    if (hdfsEncryptionShim != null) {
+      try {
+        if (hdfsEncryptionShim.isPathEncrypted(path)) {
+          return true;
+        }
+      } catch (Exception e) {
+        throw new HiveException("Unable to determine if " + path + "is 
encrypted: " + e, e);
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Compares to path key encryption strenghts.
+   *
+   * @param p1 Path to an HDFS file system
+   * @param p2 Path to an HDFS file system
+   * @return -1 if strength is weak; 0 if is equals; 1 if it is stronger
+   * @throws HiveException If an error occurs while comparing key strengths.
+   */
+  private int comparePathKeyStrength(Path p1, Path p2) throws HiveException {
+    HadoopShims.HdfsEncryptionShim hdfsEncryptionShim;
+
+    hdfsEncryptionShim = SessionState.get().getHdfsEncryptionShim();
+    if (hdfsEncryptionShim != null) {
+      try {
+        return hdfsEncryptionShim.comparePathKeyStrength(p1, p2);
+      } catch (Exception e) {
+        throw new HiveException("Unable to compare key strength for " + p1 + " 
and " + p2 + " : " + e, e);
+      }
+    }
+
+    return 0; // Non-encrypted path (or equals strength)
+  }
+
+  /**
+   * Checks if a given path has read-only access permissions.
+   *
+   * @param path The path to check for read-only permissions.
+   * @return True if the path is read-only; False otherwise.
+   * @throws HiveException If an error occurs while checking file permissions.
+   */
+  private boolean isPathReadOnly(Path path) throws HiveException {
+    HiveConf conf = SessionState.get().getConf();
+    try {
+      FileSystem fs = path.getFileSystem(conf);
+      UserGroupInformation ugi = 
ShimLoader.getHadoopShims().getUGIForConf(conf);
+      FileStatus status = fs.getFileStatus(path);
+
+      // We just check for writing permissions. If it fails with 
AccessControException, then it
+      // means the location may be read-only.
+      FileUtils.checkFileAccessWithImpersonation(fs, status, FsAction.WRITE, 
ugi.getUserName());
+
+      // Path has writing permissions
+      return false;
+    } catch (AccessControlException e) {
+      // An AccessControlException may be caused for other different errors,
+      // but we take it as if our path is read-only
+      return true;
+    } catch (Exception e) {
+      throw new HiveException("Unable to determine if " + path + " is read 
only: " + e, e);
+    }
+  }
+
+  /**
    * Gets the strongest encrypted table path.
    *
    * @param qb The QB object that contains a list of all table locations.
@@ -1887,7 +1958,6 @@ public class SemanticAnalyzer extends Ba
   private Path getStrongestEncryptedTablePath(QB qb) throws HiveException {
     List<String> tabAliases = new ArrayList<String>(qb.getTabAliases());
     Path strongestPath = null;
-    HadoopShims.HdfsEncryptionShim hdfsEncryptionShim = 
SessionState.get().getHdfsEncryptionShim();
 
     /* Walk through all found table locations to get the most encrypted table 
*/
     for (String alias : tabAliases) {
@@ -1898,45 +1968,61 @@ public class SemanticAnalyzer extends Ba
           try {
             if (strongestPath == null) {
               strongestPath = tablePath;
-            } else if (hdfsEncryptionShim != null
-                && tablePath.toUri().getScheme().equals("hdfs")
-                && hdfsEncryptionShim.isPathEncrypted(tablePath))
+            } else if (tablePath.toUri().getScheme().equals("hdfs")
+                && isPathEncrypted(tablePath)
+                && comparePathKeyStrength(tablePath, strongestPath) > 0)
             {
-              if (hdfsEncryptionShim.comparePathKeyStrength(tablePath, 
strongestPath) > 0) {
-                strongestPath = tablePath;
-              }
+              strongestPath = tablePath;
             }
-          } catch (IOException e) {
-            throw new HiveException("Cannot search for the most secure table 
path", e);
+          } catch (HiveException e) {
+            throw new HiveException("Unable to find the most secure table 
path: " + e, e);
           }
         }
       }
     }
 
-    /* Check for writing permissions on the selected location. */
-    if (strongestPath != null && 
strongestPath.toUri().getScheme().equals("hdfs")) {
-      try {
-        FileSystem fs = 
strongestPath.getFileSystem(SessionState.get().getConf());
-        UserGroupInformation ugi = 
ShimLoader.getHadoopShims().getUGIForConf(SessionState.get().getConf());
-        FileStatus status = fs.getFileStatus(strongestPath);
+    return strongestPath;
+  }
 
-        FileUtils.checkFileAccessWithImpersonation(fs, status, FsAction.WRITE, 
ugi.getUserName());
-      } catch (AccessControlException e) {
-        try {
-          if (hdfsEncryptionShim == null || 
!hdfsEncryptionShim.isPathEncrypted(strongestPath)) {
-            strongestPath = null;
+  /**
+   * Gets the staging directory where MR files will be stored temporary.
+   * It walks through the QB plan to find the correct location where save 
temporary files. This
+   * temporary location (or staging directory) may be created inside encrypted 
tables locations for
+   * security reasons. If the QB has read-only tables, then the older scratch 
directory will be used,
+   * or a permission error will be thrown if the requested query table is 
encrypted and the old scratch
+   * directory is not.
+   *
+   * @param qb The QB object that contains a list of all table locations.
+   * @return The path to the staging directory.
+   * @throws HiveException If an error occurs while identifying the correct 
staging location.
+   */
+  private Path getStagingDirectoryPathname(QB qb) throws HiveException {
+    Path stagingPath = null, tablePath;
+
+    // Looks for the most encrypted table location (if there is one)
+    tablePath = getStrongestEncryptedTablePath(qb);
+    if (tablePath != null) {
+      // Only HDFS paths can be checked for encryption
+      if (tablePath.toUri().getScheme().equals("hdfs")) {
+        if (isPathReadOnly(tablePath) && isPathEncrypted(tablePath)) {
+          Path tmpPath = ctx.getMRTmpPath();
+          if (comparePathKeyStrength(tablePath, tmpPath) < 0) {
+            throw new HiveException("Read-only encrypted tables cannot be read 
" +
+                "if the scratch directory is not encrypted (or encryption is 
weak)");
           } else {
-            throw new HiveException(e.getMessage(), e);
+            stagingPath = tmpPath;
           }
-        } catch (IOException e1) {
-          throw new HiveException(e.getMessage(), e);
         }
-      } catch (Exception e) {
-        throw new HiveException(e.getMessage(), e);
       }
+
+      if (stagingPath == null) {
+        stagingPath = ctx.getMRTmpPath(tablePath.toUri());
+      }
+    } else {
+      stagingPath = ctx.getMRTmpPath();
     }
 
-    return strongestPath;
+    return stagingPath;
   }
 
   private void replaceViewReferenceWithDefinition(QB qb, Table tab,


Reply via email to