You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sqoop.apache.org by ab...@apache.org on 2014/10/08 23:00:21 UTC

git commit: SQOOP-1471: Use Hadoop CredentialProvider API to encyrpt passwords at rest

Repository: sqoop
Updated Branches:
  refs/heads/trunk dcd5d843c -> 65a9340fc


SQOOP-1471: Use Hadoop CredentialProvider API to encyrpt passwords at rest

(Venkat Ranganathan via Abraham Elmahrek)


Project: http://git-wip-us.apache.org/repos/asf/sqoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/sqoop/commit/65a9340f
Tree: http://git-wip-us.apache.org/repos/asf/sqoop/tree/65a9340f
Diff: http://git-wip-us.apache.org/repos/asf/sqoop/diff/65a9340f

Branch: refs/heads/trunk
Commit: 65a9340fc22fcc3c1d2c64b3976994aa1c419b8f
Parents: dcd5d84
Author: Abraham Elmahrek <ab...@elmahrek.com>
Authored: Wed Oct 8 13:59:47 2014 -0700
Committer: Abraham Elmahrek <ab...@elmahrek.com>
Committed: Wed Oct 8 13:59:47 2014 -0700

----------------------------------------------------------------------
 src/docs/user/connecting.txt                    |  47 ++++++
 src/java/org/apache/sqoop/SqoopOptions.java     |   8 +
 .../org/apache/sqoop/tool/BaseSqoopTool.java    |  41 ++++-
 .../util/password/CredentialProviderHelper.java | 151 +++++++++++++++++++
 .../CredentialProviderPasswordLoader.java       |  59 ++++++++
 .../sqoop/util/password/FilePasswordLoader.java |   6 +-
 .../credentials/TestPassingSecurePassword.java  | 105 ++++++++++++-
 7 files changed, 400 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/sqoop/blob/65a9340f/src/docs/user/connecting.txt
----------------------------------------------------------------------
diff --git a/src/docs/user/connecting.txt b/src/docs/user/connecting.txt
index 6a28254..fd812c0 100644
--- a/src/docs/user/connecting.txt
+++ b/src/docs/user/connecting.txt
@@ -63,6 +63,53 @@ $ sqoop import --connect jdbc:mysql://database.example.com/employees \
 Another way of supplying passwords is using the +-P+ argument which will
 read a password from a console prompt.
 
+.Protecting password from preying eyes
+Hadoop 2.6.0 provides an API to separate password storage from applications.
+This API is called the credential provided API and there is a new
++credential+  command line tool to manage passwords and their aliases.
+The passwords are stored with their aliases in a keystore that is password
+protected.   The keystore password can be the provided to a password prompt
+on the command line, via an environment variable or defaulted to a software
+defined constant.   Please check the Hadoop documentation on the usage
+of this facility.
+
+Once the password is stored using the Credential Provider facility and
+the Hadoop configuration has been suitably updated, all applications can
+optionally use the alias in place of the actual password and at runtime
+resolve the alias for the password to use.
+
+Since the keystore or similar technology used for storing the credential
+provider is shared across components, passwords for various applications,
+various database and other passwords can be securely stored in them and only
+the alias needs to be exposed in configuration files, protecting the password
+from being visible.
+
+Sqoop has been enhanced to allow usage of this funcionality if it is
+available in the underlying Hadoop version being used.   One new option
+has been introduced to provide the alias on the command line instead of the
+actual password (--password-alias).  The argument value this option is
+the alias on the storage associated with the actual password.
+Example usage is as follows:
+
+----
+$ sqoop import --connect jdbc:mysql://database.example.com/employees \
+    --username dbuser --password-alias mydb.password.alias
+----
+
+Similarly, if the command line option is not preferred, the alias can be saved
+in the file provided with --password-file option.  Along with this, the
+Sqoop configuration parameter org.apache.sqoop.credentials.loader.class
+should be set to the classname that provides the alias resolution:
++org.apache.sqoop.util.password.CredentialProviderPasswordLoader+
+
+Example usage is as follows (assuming .password.alias has the alias for
+the real password) :
+
+----
+$ sqoop import --connect jdbc:mysql://database.example.com/employees \
+    --username dbuser --password-file ${user.home}/.password-alias
+----
+
 .Non-secure way of passing password
 
 WARNING: The +\--password+ parameter is insecure, as other users may

http://git-wip-us.apache.org/repos/asf/sqoop/blob/65a9340f/src/java/org/apache/sqoop/SqoopOptions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/sqoop/SqoopOptions.java b/src/java/org/apache/sqoop/SqoopOptions.java
index d16ccb3..0070d0b 100644
--- a/src/java/org/apache/sqoop/SqoopOptions.java
+++ b/src/java/org/apache/sqoop/SqoopOptions.java
@@ -119,6 +119,7 @@ public class SqoopOptions implements Cloneable {
   // This represents path to a file on ${user.home} containing the password
   // with 400 permissions so its only readable by user executing the tool
   @StoredAsProperty("db.password.file") private String passwordFilePath;
+  @StoredAsProperty("db.password.alias") private String passwordAlias;
 
   @StoredAsProperty("null.string") private String nullStringValue;
   @StoredAsProperty("input.null.string") private String inNullStringValue;
@@ -1183,6 +1184,13 @@ public class SqoopOptions implements Cloneable {
     this.passwordFilePath = passwdFilePath;
   }
 
+  public String getPasswordAlias() {
+    return passwordAlias;
+  }
+
+  public void setPasswordAlias(String alias) {
+    this.passwordAlias = alias;
+  }
   protected void parseColumnMapping(String mapping,
           Properties output) {
     output.clear();

http://git-wip-us.apache.org/repos/asf/sqoop/blob/65a9340f/src/java/org/apache/sqoop/tool/BaseSqoopTool.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/sqoop/tool/BaseSqoopTool.java b/src/java/org/apache/sqoop/tool/BaseSqoopTool.java
index 498ad79..b41ee2d 100644
--- a/src/java/org/apache/sqoop/tool/BaseSqoopTool.java
+++ b/src/java/org/apache/sqoop/tool/BaseSqoopTool.java
@@ -35,6 +35,7 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.sqoop.util.CredentialsUtil;
 import org.apache.sqoop.util.LoggingUtils;
+import org.apache.sqoop.util.password.CredentialProviderHelper;
 
 import com.cloudera.sqoop.ConnFactory;
 import com.cloudera.sqoop.Sqoop;
@@ -73,6 +74,7 @@ public abstract class BaseSqoopTool extends com.cloudera.sqoop.tool.SqoopTool {
   public static final String PASSWORD_ARG = "password";
   public static final String PASSWORD_PROMPT_ARG = "P";
   public static final String PASSWORD_PATH_ARG = "password-file";
+  public static final String PASSWORD_ALIAS_ARG = "password-alias";
   public static final String DIRECT_ARG = "direct";
   public static final String BATCH_ARG = "batch";
   public static final String TABLE_ARG = "table";
@@ -426,7 +428,10 @@ public abstract class BaseSqoopTool extends com.cloudera.sqoop.tool.SqoopTool {
     commonOpts.addOption(OptionBuilder
         .withDescription("Read password from console")
         .create(PASSWORD_PROMPT_ARG));
-
+    commonOpts.addOption(OptionBuilder.withArgName(PASSWORD_ALIAS_ARG)
+      .hasArg().withDescription("Credential provider password alias")
+      .withLongOpt(PASSWORD_ALIAS_ARG)
+      .create());
     commonOpts.addOption(OptionBuilder.withArgName("dir")
         .hasArg().withDescription("Override $HADOOP_MAPRED_HOME_ARG")
         .withLongOpt(HADOOP_MAPRED_HOME_ARG)
@@ -1017,9 +1022,10 @@ public abstract class BaseSqoopTool extends com.cloudera.sqoop.tool.SqoopTool {
     }
 
     if (in.hasOption(PASSWORD_PATH_ARG)) {
-      if (in.hasOption(PASSWORD_ARG) || in.hasOption(PASSWORD_PROMPT_ARG)) {
-        throw new InvalidOptionsException("Either password or path to a "
-          + "password file must be specified but not both.");
+      if (in.hasOption(PASSWORD_ARG) || in.hasOption(PASSWORD_PROMPT_ARG)
+          || in.hasOption(PASSWORD_ALIAS_ARG)) {
+        throw new InvalidOptionsException("Only one of password, password "
+          + "alias or path to a password file must be specified.");
       }
 
       try {
@@ -1029,10 +1035,31 @@ public abstract class BaseSqoopTool extends com.cloudera.sqoop.tool.SqoopTool {
         // And allow the PasswordLoader to clean up any sensitive properties
         CredentialsUtil.cleanUpSensitiveProperties(out.getConf());
       } catch (IOException ex) {
-        LOG.warn("Failed to load connection parameter file", ex);
+        LOG.warn("Failed to load password file", ex);
+        throw (InvalidOptionsException)
+          new InvalidOptionsException("Error while loading password file: "
+            + ex.getMessage()).initCause(ex);
+      }
+    }
+    if (in.hasOption(PASSWORD_ALIAS_ARG)) {
+      if (in.hasOption(PASSWORD_ARG) || in.hasOption(PASSWORD_PROMPT_ARG)
+          || in.hasOption(PASSWORD_PATH_ARG)) {
+        throw new InvalidOptionsException("Only one of password, password "
+          + "alias or path to a password file must be specified.");
+      }
+      out.setPasswordAlias(in.getOptionValue(PASSWORD_ALIAS_ARG));
+      if (!CredentialProviderHelper.isProviderAvailable()) {
         throw new InvalidOptionsException(
-          "Error while loading connection parameter file: "
-            + ex.getMessage());
+          "CredentialProvider facility not available in the hadoop "
+          + " environment used");
+      }
+      try {
+        out.setPassword(CredentialProviderHelper
+          .resolveAlias(out.getConf(), in.getOptionValue(PASSWORD_ALIAS_ARG)));
+      } catch (IOException ioe) {
+        throw (InvalidOptionsException)
+          new InvalidOptionsException("Unable to process alias")
+            .initCause(ioe);
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/sqoop/blob/65a9340f/src/java/org/apache/sqoop/util/password/CredentialProviderHelper.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/sqoop/util/password/CredentialProviderHelper.java b/src/java/org/apache/sqoop/util/password/CredentialProviderHelper.java
new file mode 100644
index 0000000..1d6481a
--- /dev/null
+++ b/src/java/org/apache/sqoop/util/password/CredentialProviderHelper.java
@@ -0,0 +1,151 @@
+/**
+ * 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.sqoop.util.password;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+
+/**
+ * Helper class for the Hadoop credential provider functionality.
+ * Reflrection to used to avoid directly referencing the classes and methods
+ * so that version dependency is not introduced as the Hadoop credential
+ * provider is only introduced in 2.6.0 and later
+ */
+public class CredentialProviderHelper {
+  public static final Log LOG =
+    LogFactory.getLog(CredentialProviderHelper.class.getName());
+
+  private static Class<?> clsCredProvider;
+  private static Class<?> clsCredProviderFactory;
+  private static Method methGetPassword;
+  private static Method methGetProviders;
+  private static Method methCreateCredEntry;
+  private static Method methFlush;
+
+  static {
+    try {
+      LOG.debug("Reflecting credential provider classes and methods");
+      clsCredProvider = Class
+        .forName("org.apache.hadoop.security.alias.CredentialProvider");
+      LOG
+        .debug("Found org.apache.hadoop.security.alias.CredentialProvider");
+      clsCredProviderFactory = Class.forName(
+        "org.apache.hadoop.security.alias.CredentialProviderFactory");
+      LOG
+        .debug("Found org.apache.hadoop.security.alias.CredentialProviderFactory");
+
+      methCreateCredEntry = clsCredProvider.getMethod("createCredentialEntry",
+        new Class[] { String.class, char[].class });
+      LOG
+        .debug("Found CredentialProvider#createCredentialEntry");
+
+      methFlush = clsCredProvider.getMethod("flush",
+        new Class[] {});
+      LOG
+        .debug("Found CredentialProvider#flush");
+
+      methGetPassword = Configuration.class.getMethod("getPassword",
+        new Class[] { String.class });
+      LOG
+        .debug("Found Configuration#getPassword");
+
+      methGetProviders = clsCredProviderFactory.getMethod("getProviders",
+        new Class[] { Configuration.class });
+      LOG
+        .debug("Found CredentialProviderFactory#getProviders");
+    } catch (ClassNotFoundException cnfe) {
+      LOG.debug("Ignoring exception", cnfe);
+    } catch (NoSuchMethodException nsme) {
+      LOG.debug("Ignoring exception", nsme);
+    }
+  }
+  // Should track what is specified in JavaKeyStoreProvider class.
+  public static final String SCHEME_NAME = "jceks";
+  // Should track what is in CredentialProvider class.
+  public static final String CREDENTIAL_PROVIDER_PATH =
+    "hadoop.security.credential.provider.path";
+
+  public static boolean isProviderAvailable() {
+
+    if (clsCredProvider == null
+      || clsCredProviderFactory == null
+      || methCreateCredEntry == null
+      || methGetPassword == null
+      || methFlush == null) {
+      return false;
+    }
+    return true;
+  }
+
+  public static String resolveAlias(Configuration conf, String alias)
+    throws IOException {
+    LOG.debug("Resolving alias with credential provider path set to "
+      + conf.get(CREDENTIAL_PROVIDER_PATH));
+    try {
+      char[] cred = (char[])
+        methGetPassword.invoke(conf, new Object[] { alias });
+      if (cred == null) {
+        throw new IOException("The provided alias cannot be resolved");
+      }
+      String pass = new String(cred);
+      return pass;
+    } catch (InvocationTargetException ite) {
+      throw new RuntimeException("Error resolving password "
+        + " from the credential providers ", ite.getTargetException());
+    } catch (IllegalAccessException iae) {
+      throw new RuntimeException("Error invoking the credential provider method",
+        iae);
+    }
+  }
+  /**
+   * Test utility to create an entry
+   */
+  public static void createCredentialEntry(Configuration conf,
+
+    String alias, String credential) throws IOException {
+
+    if (!isProviderAvailable()) {
+      throw new RuntimeException("CredentialProvider facility not available "
+        + "in the hadoop environment");
+    }
+
+
+    try {
+      List<?> result = (List<?>)
+        methGetProviders.invoke(null, new Object[] { conf });
+      Object provider = result.get(0);
+      LOG.debug("Using credential provider " + provider);
+
+      methCreateCredEntry.invoke(provider, new Object[] {
+        alias, credential.toCharArray() });
+      methFlush.invoke(provider, new Object[] {});
+    } catch (InvocationTargetException ite) {
+      throw new RuntimeException("Error creating credential entry "
+        + " using the credentail provider", ite.getTargetException());
+    } catch (IllegalAccessException iae) {
+      throw new RuntimeException("Error accessing the credential create method",
+        iae);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/65a9340f/src/java/org/apache/sqoop/util/password/CredentialProviderPasswordLoader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/sqoop/util/password/CredentialProviderPasswordLoader.java b/src/java/org/apache/sqoop/util/password/CredentialProviderPasswordLoader.java
new file mode 100644
index 0000000..501f130
--- /dev/null
+++ b/src/java/org/apache/sqoop/util/password/CredentialProviderPasswordLoader.java
@@ -0,0 +1,59 @@
+/**
+ * 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.sqoop.util.password;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+
+/**
+ * A password loader that loads an credential provider alias.
+ * The alias is  resolved using the Hadoop credential provider facilitity
+ * if available.
+ */
+public class CredentialProviderPasswordLoader extends FilePasswordLoader {
+  public static final Log LOG =
+    LogFactory.getLog(CredentialProviderPasswordLoader.class.getName());
+
+  /**
+   * If credential provider is available (made available as part of 2.6.0 and
+   * 3.0, then use the credential provider to get the password. Else throw an
+   * exception saying this provider is not available.
+   */
+  @Override
+  public String loadPassword(String p, Configuration configuration)
+    throws IOException {
+    if (!CredentialProviderHelper.isProviderAvailable()) {
+      throw new IOException("CredentialProvider facility not available "
+        + "in the hadoop environment used");
+    }
+    LOG.debug("Fetching alias from the specified path: " + p);
+    Path path = new Path(p);
+    FileSystem fs = path.getFileSystem(configuration);
+
+    // Not closing FileSystem object because of SQOOP-1226
+    verifyPath(fs, path);
+    String alias = new String(readBytes(fs, path));
+    String pass = CredentialProviderHelper.resolveAlias(configuration, alias);
+    return pass;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sqoop/blob/65a9340f/src/java/org/apache/sqoop/util/password/FilePasswordLoader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/sqoop/util/password/FilePasswordLoader.java b/src/java/org/apache/sqoop/util/password/FilePasswordLoader.java
index 4a288bf..7f0c8b4 100644
--- a/src/java/org/apache/sqoop/util/password/FilePasswordLoader.java
+++ b/src/java/org/apache/sqoop/util/password/FilePasswordLoader.java
@@ -48,11 +48,13 @@ public class FilePasswordLoader extends PasswordLoader {
    */
   protected void verifyPath(FileSystem fs, Path path) throws IOException {
     if (!fs.exists(path)) {
-      throw new IOException("The password file does not exist! " + path);
+      throw new IOException("The provided password file " + path
+        + " does not exist!");
     }
 
     if (!fs.isFile(path)) {
-      throw new IOException("The password file cannot be a directory! " + path);
+      throw new IOException("The provided password file " + path
+        + " is a directory!");
     }
   }
 

http://git-wip-us.apache.org/repos/asf/sqoop/blob/65a9340f/src/test/org/apache/sqoop/credentials/TestPassingSecurePassword.java
----------------------------------------------------------------------
diff --git a/src/test/org/apache/sqoop/credentials/TestPassingSecurePassword.java b/src/test/org/apache/sqoop/credentials/TestPassingSecurePassword.java
index bbf82f4..5b170b6 100644
--- a/src/test/org/apache/sqoop/credentials/TestPassingSecurePassword.java
+++ b/src/test/org/apache/sqoop/credentials/TestPassingSecurePassword.java
@@ -21,6 +21,7 @@ package org.apache.sqoop.credentials;
 import com.cloudera.sqoop.SqoopOptions;
 import com.cloudera.sqoop.testutil.BaseSqoopTestCase;
 import com.cloudera.sqoop.testutil.CommonArgs;
+
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
@@ -29,13 +30,17 @@ import org.apache.hadoop.mapred.JobConf;
 import org.apache.sqoop.mapreduce.db.DBConfiguration;
 import org.apache.sqoop.tool.BaseSqoopTool;
 import org.apache.sqoop.tool.ImportTool;
+import org.apache.sqoop.util.password.CredentialProviderHelper;
+import org.apache.sqoop.util.password.CredentialProviderPasswordLoader;
 import org.apache.sqoop.util.password.CryptoFileLoader;
+import org.apache.sqoop.util.password.PasswordLoader;
 
 import javax.crypto.Cipher;
 import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.SecretKeySpec;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -93,9 +98,10 @@ public class TestPassingSecurePassword extends BaseSqoopTestCase {
       SqoopOptions opts = getSqoopOptions(conf);
       ImportTool importTool = new ImportTool();
       importTool.parseArguments(argv, conf, opts, true);
-      fail("The password file does not exist! ");
+      fail("The password file does not exist!");
     } catch (Exception e) {
-      assertTrue(e.getMessage().contains("The password file does not exist!"));
+      assertTrue(e.getMessage().matches(".*The provided password file "
+        + ".* does not exist!"));
     }
   }
 
@@ -110,10 +116,10 @@ public class TestPassingSecurePassword extends BaseSqoopTestCase {
       SqoopOptions opts = getSqoopOptions(conf);
       ImportTool importTool = new ImportTool();
       importTool.parseArguments(argv, conf, opts, true);
-      fail("The password file cannot be a directory! ");
+      fail("The password file cannot be a directory!");
     } catch (Exception e) {
-      assertTrue(e.getMessage().contains("The password file cannot "
-        + "be a directory!"));
+      assertTrue(e.getMessage().matches(".*The provided password file .*"
+        + " is a directory!"));
     }
   }
 
@@ -137,10 +143,11 @@ public class TestPassingSecurePassword extends BaseSqoopTestCase {
       SqoopOptions out = importTool.parseArguments(argv, conf, in, true);
       assertNotNull(out.getPassword());
       importTool.validateOptions(out);
-      fail("Either password or passwordPath must be specified but not both.");
+      fail("Only one of password, password "
+          + "alias or path to a password file must be specified.");
     } catch (Exception e) {
-      assertTrue(e.getMessage().contains("Either password or path to a "
-        + "password file must be specified but not both"));
+      assertTrue(e.getMessage().contains("Only one of password, password "
+          + "alias or path to a password file must be specified."));
     }
   }
 
@@ -351,6 +358,88 @@ public class TestPassingSecurePassword extends BaseSqoopTestCase {
     }
   }
 
+  public void testCredentialProviderLoader() throws Exception {
+    CredentialProviderPasswordLoader pl =
+        new CredentialProviderPasswordLoader();
+
+    if (!CredentialProviderHelper.isProviderAvailable()) {
+      LOG.info("CredentialProvider facility not available "
+        + "in the hadoop environment used");
+    } else {
+      String alias = "super.secret.alias";
+      String pw = "super.secret.password";
+
+      String passwordFilePath = TEMP_BASE_DIR + ".pwd";
+      String jksFile = "creds.jks";
+      createTempFile(passwordFilePath);
+      writeToFile(passwordFilePath, alias.getBytes());
+      File credDir = new File(".");
+
+      Configuration conf = getConf();
+      String ourUrl =  CredentialProviderHelper.SCHEME_NAME +
+        "://file/" + credDir.getAbsolutePath() + "/" + jksFile;
+      File file = new File(credDir, jksFile);
+      file.delete();
+      conf.set(CredentialProviderHelper.CREDENTIAL_PROVIDER_PATH,
+        ourUrl);
+      CredentialProviderHelper.createCredentialEntry(conf, alias, pw);
+
+      conf.set("org.apache.sqoop.credentials.loader.class",
+        CredentialProviderPasswordLoader.class.getCanonicalName());
+
+      ArrayList<String> extraArgs = new ArrayList<String>();
+      extraArgs.add("--username");
+      extraArgs.add("username");
+      extraArgs.add("--password-file");
+      extraArgs.add(passwordFilePath);
+      String[] commonArgs = getCommonArgs(false, extraArgs);
+
+      SqoopOptions in = getSqoopOptions(conf);
+      ImportTool importTool = new ImportTool();
+
+      SqoopOptions out = importTool.parseArguments(commonArgs, conf, in, true);
+      assertEquals(pw, pl.loadPassword(passwordFilePath, conf));
+      assertEquals(pw, out.getPassword());
+    }
+  }
+
+  public void testPasswordAliasOption() throws Exception {
+    CredentialProviderPasswordLoader pl =
+        new CredentialProviderPasswordLoader();
+
+    if (!CredentialProviderHelper.isProviderAvailable()) {
+      LOG.info("CredentialProvider facility not available "
+        + "in the hadoop environment used");
+    } else {
+      String alias = "super.secret.alias";
+      String pw = "super.secret.password";
+      String jksFile = "creds.jks";
+      File credDir = new File(".");
+
+      Configuration conf = getConf();
+      String ourUrl =  CredentialProviderHelper.SCHEME_NAME +
+        "://file/" + credDir.getAbsolutePath() + "/" + jksFile;
+      File file = new File(credDir, jksFile);
+      file.delete();
+      conf.set(CredentialProviderHelper.CREDENTIAL_PROVIDER_PATH,
+        ourUrl);
+      CredentialProviderHelper.createCredentialEntry(conf, alias, pw);
+
+      ArrayList<String> extraArgs = new ArrayList<String>();
+      extraArgs.add("--username");
+      extraArgs.add("username");
+      extraArgs.add("--password-alias");
+      extraArgs.add(alias);
+      String[] commonArgs = getCommonArgs(false, extraArgs);
+
+      SqoopOptions in = getSqoopOptions(conf);
+      ImportTool importTool = new ImportTool();
+
+      SqoopOptions out = importTool.parseArguments(commonArgs, conf, in, true);
+      assertEquals(pw, out.getPassword());
+    }
+  }
+
   public void executeCipherTest(String password, String passphrase, String cipher, int keySize) throws Exception {
     LOG.info("Using cipher: " + cipher + " with keySize " + keySize + " and passphrase " + passphrase );
     String passwordFilePath = TEMP_BASE_DIR + ".pwd";