/**
* 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.ambari.server.credentialapi;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.util.ToolRunner;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
/**
* Tests CredentialUtilTest.
*/
public class CredentialUtilTest {
/**
* Cleans up itself after a test method is run.
*/
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
/**
* Redirect System.out to a stream. CredentialShell() writes to System.out.
* We want to capture that.
*/
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
/**
* Redirect System.err to a stream.
*/
private final ByteArrayOutputStream err = new ByteArrayOutputStream();
/**
* CRUD command verbs
*/
private static final String CREATE_VERB = "create";
private static final String DELETE_VERB = "delete";
private static final String LIST_VERB = "list";
private static final String GET_VERB = "get";
/**
* Gets a temporary folder name.
*
* @return
*/
private String getTempFolderName() {
return temporaryFolder.getRoot().getAbsolutePath();
}
/**
* Constructs a valid JCEKS provider path from the specified file name.
*
* @param fileName
* @return
*/
private String getProviderPath(String fileName) {
return CredentialUtil.jceksPrefix + getTempFolderName() + "/" + fileName;
}
/**
* Constructs the create arguments to create a new credential.
*
* @param alias
* @param credential
* @param providerPath
* @return
*/
private String[] getCreateArgs(String alias, String credential, String providerPath) {
List<String> args = new ArrayList<>();
args.add(CREATE_VERB);
args.add(alias);
args.add("-value");
args.add(credential);
args.add("-provider");
args.add(providerPath);
return args.toArray(new String[args.size()]);
}
/**
* Constructs the create arguments with no overwrite existing option.
*
* @param alias
* @param credential
* @param providerPath
* @return
*/
private String[] getSafeCreateArgs(String alias, String credential, String providerPath) {
List<String> args = new ArrayList<>();
args.add(CREATE_VERB);
args.add(alias);
args.add("-value");
args.add(credential);
args.add("-provider");
args.add(providerPath);
args.add("-n"); /* do not overwrite if credential exists */
return args.toArray(new String[args.size()]);
}
/**
* Constructs the create arguments with overwrite.
*
* @param alias
* @param credential
* @param providerPath
* @return
*/
private String[] getUpdateArgs(String alias, String credential, String providerPath) {
List<String> args = new ArrayList<>();
args.add(CREATE_VERB);
args.add(alias);
args.add("-value");
args.add(credential);
args.add("-provider");
args.add(providerPath);
args.add("-f"); /* overwrite */
return args.toArray(new String[args.size()]);
}
/**
* Constructs the delete arguments.
*
* @param alias
* @param providerPath
* @return
*/
private String[] getDeleteArgs(String alias, String providerPath) {
List<String> args = new ArrayList<>();
args.add(DELETE_VERB);
args.add(alias);
args.add("-provider");
args.add(providerPath);
args.add("-f"); /* suppress prompt to confirm */
return args.toArray(new String[args.size()]);
}
/**
* List arguments.
*
* @param providerPath
* @return
*/
private String[] getListArgs(String providerPath) {
List<String> args = new ArrayList<>();
args.add(LIST_VERB);
args.add("-provider");
args.add(providerPath);
return args.toArray(new String[args.size()]);
}
/**
* Get arguments.
*
* @param alias
* @param providerPath
* @return
*/
private String[] getGetArgs(String alias, String providerPath) {
List<String> args = new ArrayList<>();
args.add(GET_VERB);
args.add(alias);
args.add("-provider");
args.add(providerPath);
return args.toArray(new String[args.size()]);
}
/**
* Executes the given arguments.
*
* @param args
* @return
* @throws Exception
*/
private int executeCommand(String[] args) throws Exception {
return ToolRunner.run(new Configuration(), new CredentialUtil(), args);
}
/**
* Set up the streams before each test.
*/
@Before
public void setupStreams() {
System.setOut(new PrintStream(out));
System.setErr(new PrintStream(err));
}
/**
* Clean up the stream after each test.
*/
@After
public void teardownStreams() {
System.setOut(null);
System.setErr(null);
}
/**
* Creates a non-existing credential
*
* @throws Exception
*/
@Test
public void testCreateCommand() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String credential = "MyTopSecretPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args = getCreateArgs(alias, credential, providerPath);
int exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
}
/**
* Overwrites an existing credential.
*
* @throws Exception
*/
@Test
public void testCreateCommandOverwriteExisting() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String credential = "MyTopSecretPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
/*
* Create a new credential
*/
args = getCreateArgs(alias, credential, providerPath);
exitCode = ToolRunner.run(new Configuration(), new CredentialUtil(), args);
Assert.assertEquals(exitCode, 0);
/*
* Update the created credential
*/
credential = "MyUpdatedTopSecretPassword";
args = getUpdateArgs(alias, credential, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
}
/**
* Updates a non-existing credential. Should create it.
*
* @throws Exception
*/
@Test
public void testCreateCommandOverwriteNonExisting() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String credential = "MyTopSecretPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
/*
* Update a non-existing credential. Should create it.
*/
args = getUpdateArgs(alias, credential, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
}
/**
* Safely creates a credential. If credential already exists, nothing is done.
*
* @throws Exception
*/
@Test
public void testSafeCreateCommandExisting() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String credential = "MyTopSecretPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
/*
* Create a new credential
*/
args = getCreateArgs(alias, credential, providerPath);
exitCode = ToolRunner.run(new Configuration(), new CredentialUtil(), args);
Assert.assertEquals(exitCode, 0);
/*
* Safely update the previously created credential. Nothing is done.
*/
credential = "AnotherTopSecretPassword";
args = getSafeCreateArgs(alias, credential, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
}
/**
* Safely creates a credential. If it does not exist, it will be created.
*
* @throws Exception
*/
@Test
public void testSafeCreateCommandNotExisting() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String credential = "MyTopSecretPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
/*
* Safely update a non-existing credential. Should create the credential.
*/
credential = "AnotherTopSecretPassword";
args = getSafeCreateArgs(alias, credential, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
}
/**
* Delete an existing credential
*
* @throws Exception
*/
@Test
public void testDeleteCommandExisting() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String credential = "MyTopSecretPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
/*
* Create a new credential
*/
args = getCreateArgs(alias, credential, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
/*
* Delete the above credential
*/
args = getDeleteArgs(alias, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
}
/**
* Delete a non-existing credential
*
* @throws Exception
*/
@Test
public void testDeleteCommandNonExisting() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
/*
* Delete a non-existing credential. Should fail with exit code 1.
*/
args = getDeleteArgs(alias, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 1);
}
/**
* Retrieve an existing credential.
*
* @throws Exception
*/
@Test
public void testGetCommandExisting() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String credential = "MyTopSecretPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
/*
* Create a new credential
*/
args = getCreateArgs(alias, credential, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
out.reset();
/*
* Get the existing credential.
*/
args = getGetArgs(alias, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
String retrievedCredential = out.toString().trim();
Assert.assertEquals(credential, retrievedCredential);
}
/**
* Retrieve a non-existing credential.
*
* @throws Exception
*/
@Test
public void testGetCommandNonExisting() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
/*
* Get a non-existing credential. Should fail with exit code 1.
*/
args = getGetArgs(alias, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 1);
}
/**
* Create, delete and attempt to get the alias.
*
* @throws Exception
*/
@Test
public void testGetCommandAfterDeletion() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String credential = "MyTopSecretPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
/*
* Create a new credential
*/
args = getCreateArgs(alias, credential, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
/*
* Delete the above credential
*/
args = getDeleteArgs(alias, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
/*
* Get the existing credential. Should not be there.
*/
args = getGetArgs(alias, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 1);
}
/**
* Execute get on an invalid provider path.
*
* @throws Exception
*/
@Test
public void testGetCommandWithNonExistingProvider() throws Exception {
String alias = "javax.jdo.option.ConnectionPassword";
String credential = "MyTopSecretPassword";
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
/*
* Create a new credential
*/
args = getCreateArgs(alias, credential, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
/*
* Get a credential. Should not be there.
*/
args = getGetArgs(alias, "BadProvider.jceks");
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 1);
}
/**
* List all aliases.
*
* @throws Exception
*/
@Test
public void testListCommand() throws Exception {
String providerPath = getProviderPath("CreateCommandTest.jceks");
String[] args;
int exitCode;
final int numEntries = 5;
Properties properties = new Properties();
/*
* Create some alias password entries.
*/
for (int i = 0; i < numEntries; ++i) {
String alias = String.format("alias_%d", i + 1);
String credential = String.format("credential_%d", i + 1);
properties.setProperty(alias, credential);
args = getCreateArgs(alias, credential, providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
}
out.reset();
/*
* List all aliases.
*/
args = getListArgs(providerPath);
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
List<String> aliases = Arrays.asList(out.toString().split(System.getProperty("line.separator")));
Enumeration enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String alias = (String)enumeration.nextElement();
Assert.assertTrue(aliases.contains(alias));
}
}
/**
* Prints the tool usage.
*
* @throws Exception
*/
@Test
public void testToolUsage() throws Exception {
String[] args = new String[1];
int exitCode;
/*
* Invoke tool help
*/
args[0] = "-help";
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
Assert.assertTrue(!out.toString().isEmpty());
}
/**
* Invoke Create command help
*
* @throws Exception
*/
@Test
public void testCreateCommandUsage() throws Exception {
String[] args = new String[2];
int exitCode;
args[0] = CREATE_VERB;
args[1] = "-help";
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
Assert.assertTrue(!out.toString().isEmpty());
}
/*
* Invoke Delete command help
*
* @throws Exception
*/
@Test
public void testDeleteCommandUsage() throws Exception {
String[] args = new String[2];
int exitCode;
args[0] = DELETE_VERB;
args[1] = "-help";
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
Assert.assertTrue(!out.toString().isEmpty());
}
/*
* Invoke List command help
*
* @throws Exception
*/
@Test
public void testListCommandUsage() throws Exception {
String[] args = new String[2];
int exitCode;
args[0] = LIST_VERB;
args[1] = "-help";
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
Assert.assertTrue(!out.toString().isEmpty());
}
/*
* Invoke Get command help
*
* @throws Exception
*/
@Test
public void testGetCommandUsage() throws Exception {
String[] args = new String[2];
int exitCode;
args[0] = GET_VERB;
args[1] = "-help";
exitCode = executeCommand(args);
Assert.assertEquals(exitCode, 0);
Assert.assertTrue(!out.toString().isEmpty());
}
}