/** * 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()); } }