This is an automated email from the ASF dual-hosted git repository.

stevel pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/hadoop.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 2eba2624 HADOOP-16615. Add password check for credential provider,
2eba2624 is described below

commit 2eba2624723e2258bddef72d05a1e90576082315
Author: hongdongdong <[email protected]>
AuthorDate: Thu Oct 24 18:48:27 2019 +0100

    HADOOP-16615. Add password check for credential provider,
    
    Contributed by hongdongdong.
    
    Change-Id: Iaac01bc8594860064a80c822a0e47981243ab7e1
---
 .../hadoop/security/alias/CredentialShell.java     | 94 +++++++++++++++++++++-
 .../src/site/markdown/CommandsManual.md            |  3 +
 .../hadoop/security/alias/TestCredShell.java       | 31 +++++--
 3 files changed, 121 insertions(+), 7 deletions(-)

diff --git 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialShell.java
 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialShell.java
index 5696118..6037724 100644
--- 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialShell.java
+++ 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialShell.java
@@ -44,7 +44,8 @@ public class CredentialShell extends CommandShell {
       "   [-help]\n" +
       "   [" + CreateCommand.USAGE + "]\n" +
       "   [" + DeleteCommand.USAGE + "]\n" +
-      "   [" + ListCommand.USAGE + "]\n";
+      "   [" + ListCommand.USAGE + "]\n" +
+      "   [" + CheckCommand.USAGE + "]\n";
   @VisibleForTesting
   public static final String NO_VALID_PROVIDERS =
       "There are no valid (non-transient) providers configured.\n" +
@@ -66,6 +67,7 @@ public class CredentialShell extends CommandShell {
    * <pre>
    * % hadoop credential create alias [-provider providerPath]
    * % hadoop credential list [-provider providerPath]
+   * % hadoop credential check alias [-provider providerPath]
    * % hadoop credential delete alias [-provider providerPath] [-f]
    * </pre>
    * @param args
@@ -86,6 +88,11 @@ public class CredentialShell extends CommandShell {
           return 1;
         }
         setSubCommand(new CreateCommand(args[++i]));
+      } else if (args[i].equals("check")) {
+        if (i == args.length - 1) {
+          return 1;
+        }
+        setSubCommand(new CheckCommand(args[++i]));
       } else if (args[i].equals("delete")) {
         if (i == args.length - 1) {
           return 1;
@@ -293,6 +300,91 @@ public class CredentialShell extends CommandShell {
     }
   }
 
+  private class CheckCommand extends Command {
+    public static final String USAGE = "check <alias> [-value alias-value] " +
+        "[-provider provider-path] [-strict]";
+    public static final String DESC =
+        "The check subcommand check a password for the name\n" +
+        "specified as the <alias> argument within the provider indicated\n" +
+        "through the -provider argument. If -strict is supplied, fail\n" +
+        "immediately if the provider requires a password and none is given.\n" 
+
+        "If -value is provided, use that for the value of the credential\n" +
+        "instead of prompting the user.";
+
+    private String alias = null;
+
+    CheckCommand(String alias) {
+      this.alias = alias;
+    }
+
+    public boolean validate() {
+      if (alias == null) {
+        getOut().println("There is no alias specified. Please provide the" +
+            "mandatory <alias>. See the usage description with -help.");
+        return false;
+      }
+      if (alias.equals("-help")) {
+        return true;
+      }
+      try {
+        provider = getCredentialProvider();
+        if (provider == null) {
+          return false;
+        } else if (provider.needsPassword()) {
+          if (strict) {
+            getOut().println(provider.noPasswordError());
+            return false;
+          } else {
+            getOut().println(provider.noPasswordWarning());
+          }
+        }
+      } catch (IOException e) {
+        e.printStackTrace(getErr());
+      }
+      return true;
+    }
+
+    public void execute() throws IOException, NoSuchAlgorithmException {
+      if (alias.equals("-help")) {
+        doHelp();
+        return;
+      }
+      warnIfTransientProvider();
+      getOut().println("Checking aliases for CredentialProvider: " +
+          provider.toString());
+      try {
+        PasswordReader c = getPasswordReader();
+        if (c == null) {
+          throw new IOException("No console available for checking user.");
+        }
+
+        char[] password = null;
+        if (value != null) {
+          // testing only
+          password = value.toCharArray();
+        } else {
+          password = c.readPassword("Enter alias password: ");
+        }
+        char[] storePassword =
+            provider.getCredentialEntry(alias).getCredential();
+        String beMatch =
+            Arrays.equals(storePassword, password) ? "success" : "failed";
+
+        getOut().println("Password match " + beMatch + " for " +  alias + ".");
+      } catch (IOException e) {
+        getOut().println("Cannot check aliases for CredentialProvider: " +
+            provider.toString()
+            + ": " + e.getMessage());
+        throw e;
+      }
+    }
+
+    @Override
+    public String getUsage() {
+      return USAGE + ":\n\n" + DESC;
+    }
+  }
+
   private class CreateCommand extends Command {
     public static final String USAGE = "create <alias> [-value alias-value] " +
         "[-provider provider-path] [-strict]";
diff --git 
a/hadoop-common-project/hadoop-common/src/site/markdown/CommandsManual.md 
b/hadoop-common-project/hadoop-common/src/site/markdown/CommandsManual.md
index f39a92d..0bda253 100644
--- a/hadoop-common-project/hadoop-common/src/site/markdown/CommandsManual.md
+++ b/hadoop-common-project/hadoop-common/src/site/markdown/CommandsManual.md
@@ -125,6 +125,7 @@ Usage: `hadoop credential <subcommand> [options]`
 | create *alias* [-provider *provider-path*] [-strict] [-value 
*credential-value*] | Prompts the user for a credential to be stored as the 
given alias. The *hadoop.security.credential.provider.path* within the 
core-site.xml file will be used unless a `-provider` is indicated. The 
`-strict` flag will cause the command to fail if the provider uses a default 
password. Use `-value` flag to supply the credential value (a.k.a. the alias 
password) instead of being prompted. |
 | delete *alias* [-provider *provider-path*] [-strict] [-f] | Deletes the 
credential with the provided alias. The 
*hadoop.security.credential.provider.path* within the core-site.xml file will 
be used unless a `-provider` is indicated. The `-strict` flag will cause the 
command to fail if the provider uses a default password. The command asks for 
confirmation unless `-f` is specified |
 | list [-provider *provider-path*] [-strict] | Lists all of the credential 
aliases The *hadoop.security.credential.provider.path* within the core-site.xml 
file will be used unless a `-provider` is indicated. The `-strict` flag will 
cause the command to fail if the provider uses a default password. |
+| check *alias* [-provider *provider-path*] [-strict] | Check the password for 
the given alias. The *hadoop.security.credential.provider.path* within the 
core-site.xml file will be used unless a `-provider` is indicated. The 
`-strict` flag will cause the command to fail if the provider uses a default 
password. |
 
 Command to manage credentials, passwords and secrets within credential 
providers.
 
@@ -221,6 +222,8 @@ Usage: `hadoop key <subcommand> [options]`
 | roll *keyname* [-provider *provider*] [-strict] [-help] | Creates a new 
version for the specified key within the provider indicated using the 
`-provider` argument. The `-strict` flag will cause the command to fail if the 
provider uses a default password. |
 | delete *keyname* [-provider *provider*] [-strict] [-f] [-help] | Deletes all 
versions of the key specified by the *keyname* argument from within the 
provider specified by `-provider`. The `-strict` flag will cause the command to 
fail if the provider uses a default password. The command asks for user 
confirmation unless `-f` is specified. |
 | list [-provider *provider*] [-strict] [-metadata] [-help] | Displays the 
keynames contained within a particular provider as configured in core-site.xml 
or specified with the `-provider` argument. The `-strict` flag will cause the 
command to fail if the provider uses a default password. `-metadata` displays 
the metadata. |
+| check *keyname* [-provider *provider*] [-strict] [-help] | Check password of 
the *keyname* contained within a particular provider as configured in 
core-site.xml or specified with the `-provider` argument. The `-strict` flag 
will cause the command to fail if the provider uses a default password. |
+
 | -help | Prints usage of this command |
 
 Manage keys via the KeyProvider. For details on KeyProviders, see the 
[Transparent Encryption Guide](../hadoop-hdfs/TransparentEncryption.html).
diff --git 
a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredShell.java
 
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredShell.java
index 569fe73..bf72b52 100644
--- 
a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredShell.java
+++ 
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredShell.java
@@ -32,6 +32,7 @@ import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.security.ProviderUtils;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.assertj.core.api.Assertions;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -43,6 +44,11 @@ public class TestCredShell {
   /* The default JCEKS provider - for testing purposes */
   private String jceksProvider;
 
+  private void assertOutputContains(String expected) {
+    Assertions.assertThat(outContent.toString())
+      .contains(expected);
+  }
+
   @Before
   public void setup() throws Exception {
     System.setOut(new PrintStream(outContent));
@@ -172,15 +178,28 @@ public class TestCredShell {
     shell.setPasswordReader(new MockPasswordReader(passwords));
     rc = shell.run(args1);
     assertEquals(0, rc);
-    assertTrue(outContent.toString().contains("credential1 has been 
successfully " +
-        "created."));
-    
-    String[] args2 = {"delete", "credential1", "-f", "-provider",
+    assertOutputContains("credential1 has been successfully created.");
+
+    String[] args2 = {"check", "credential1", "-provider",
         jceksProvider};
+    ArrayList<String> password = new ArrayList<String>();
+    password.add("p@ssw0rd");
+    shell.setPasswordReader(new MockPasswordReader(password));
     rc = shell.run(args2);
     assertEquals(0, rc);
-    assertTrue(outContent.toString().contains("credential1 has been 
successfully " +
-        "deleted."));
+    assertOutputContains("Password match success for credential1.");
+    ArrayList<String> passwordError = new ArrayList<String>();
+    passwordError.add("p@ssw0rderr");
+    shell.setPasswordReader(new MockPasswordReader(password));
+    rc = shell.run(args2);
+    assertEquals(0, rc);
+    assertOutputContains("Password match failed for credential1.");
+
+    String[] args3 = {"delete", "credential1", "-f", "-provider",
+        jceksProvider};
+    rc = shell.run(args3);
+    assertEquals(0, rc);
+    assertOutputContains("credential1 has been successfully deleted.");
   }
   
   public class MockPasswordReader extends CredentialShell.PasswordReader {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to