HADOOP-16615. Add password check for credential provider,
Contributed by hongdongdong. Change-Id: Iaac01bc8594860064a80c822a0e47981243ab7e1
This commit is contained in:
parent
ac6b6a6a85
commit
2eba262472
|
@ -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]";
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue