Browse Source

AMBARI-12772. Adding host via blueprint fails on secure cluster (rlevas)

Robert Levas 10 năm trước cách đây
mục cha
commit
e681f2bf4d
23 tập tin đã thay đổi với 1338 bổ sung692 xóa
  1. 45 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
  2. 29 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
  3. 93 74
      ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
  4. 36 30
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java
  5. 34 5
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreService.java
  6. 203 123
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java
  7. 151 0
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/FileBasedCredentialStoreService.java
  8. 142 0
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/InMemoryCredentialStoreService.java
  9. 209 172
      ambari-server/src/main/java/org/apache/ambari/server/security/encryption/MasterKeyServiceImpl.java
  10. 42 12
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosCredential.java
  11. 6 8
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
  12. 8 22
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
  13. 2 2
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandler.java
  14. 1 1
      ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java
  15. 29 75
      ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
  16. 27 47
      ambari-server/src/test/java/org/apache/ambari/server/security/encryption/CredentialProviderTest.java
  17. 187 57
      ambari-server/src/test/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceTest.java
  18. 46 23
      ambari-server/src/test/java/org/apache/ambari/server/security/encryption/MasterKeyServiceTest.java
  19. 9 9
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java
  20. 3 3
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosCredentialTest.java
  21. 4 4
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerTest.java
  22. 20 12
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerActionTest.java
  23. 12 12
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandlerTest.java

+ 45 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java

@@ -118,6 +118,7 @@ import org.apache.ambari.server.security.authorization.Users;
 import org.apache.ambari.server.security.ldap.AmbariLdapDataPopulator;
 import org.apache.ambari.server.security.ldap.LdapBatchDto;
 import org.apache.ambari.server.security.ldap.LdapSyncDto;
+import org.apache.ambari.server.serveraction.kerberos.KerberosCredential;
 import org.apache.ambari.server.serveraction.kerberos.KerberosInvalidConfigurationException;
 import org.apache.ambari.server.serveraction.kerberos.KerberosOperationException;
 import org.apache.ambari.server.stageplanner.RoleGraph;
@@ -1258,7 +1259,50 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
         throw new AmbariException("The cluster may not be null");
       }
 
-      cluster.addSessionAttributes(request.getSessionAttributes());
+      Map<String, Object> sessionAttributes = request.getSessionAttributes();
+
+      // TODO: Create REST API entry point to securely set credentials in the CredentialProvider then
+      // TODO: remove this block to _clean_ the session attributes and store any KDC administrator
+      // TODO: credentials in the secure credential provider facility.
+      // For now, to keep things backwards compatible, get and remove the KDC administrator credentials
+      // from the session attributes and store them in the CredentialsProvider. The KDC administrator
+      // credentials are prefixed with kdc_admin/. The following attributes are expected, if setting
+      // the KDC administrator credentials:
+      //    kerberos_admin/principal
+      //    kerberos_admin/password
+      if((sessionAttributes != null) && !sessionAttributes.isEmpty()) {
+        Map<String, Object> cleanedSessionAttributes = new HashMap<String, Object>();
+        String principal = null;
+        char[] password = null;
+
+        for(Map.Entry<String,Object> entry: sessionAttributes.entrySet()) {
+          String name = entry.getKey();
+          Object value = entry.getValue();
+
+          if ("kerberos_admin/principal".equals(name)) {
+            if(value instanceof String) {
+              principal = (String)value;
+            }
+          }
+          else if ("kerberos_admin/password".equals(name)) {
+            if(value instanceof String) {
+              password = ((String) value).toCharArray();
+            }
+          } else {
+            cleanedSessionAttributes.put(name, value);
+          }
+        }
+
+        if(principal != null) {
+          // The KDC admin principal exists... set the credentials in the credentials store
+          kerberosHelper.setKDCCredentials(new KerberosCredential(principal, password, null));
+        }
+
+        sessionAttributes = cleanedSessionAttributes;
+      }
+      // TODO: END
+
+      cluster.addSessionAttributes(sessionAttributes);
       //
       // ***************************************************
 

+ 29 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java

@@ -21,6 +21,7 @@ package org.apache.ambari.server.controller;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.internal.RequestStageContainer;
 import org.apache.ambari.server.serveraction.kerberos.KerberosAdminAuthenticationException;
+import org.apache.ambari.server.serveraction.kerberos.KerberosCredential;
 import org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileWriter;
 import org.apache.ambari.server.serveraction.kerberos.KerberosInvalidConfigurationException;
 import org.apache.ambari.server.serveraction.kerberos.KerberosMissingAdminCredentialsException;
@@ -415,6 +416,34 @@ public interface KerberosHelper {
                                                                           boolean replaceHostNames)
       throws AmbariException;
 
+  /**
+   * Sets the KDC administrator credentials.
+   * <p/>
+   * It is up to the implementation to determine how to store
+   * these credentials and for how long.
+   *
+   * @param credentials the KDC administrator credentials
+   * @throws AmbariException if an error occurs while storing the credentials
+   */
+  void setKDCCredentials(KerberosCredential credentials) throws AmbariException;
+
+  /**
+   * Removes the previously set KDC administrator credentials.
+   *
+   * @throws AmbariException if an error occurs while removing the credentials
+   * @see KerberosHelper#setKDCCredentials(KerberosCredential)
+   */
+  void removeKDCCredentials() throws AmbariException;
+
+  /**
+   * Gets the previously stored KDC administrator credentials.
+   *
+   * @return a KerberosCredential or null, if the KDC administrator credentials have not be set or
+   * have been removed
+   * @throws AmbariException if an error occurs while retrieving the credentials
+   * @see KerberosHelper#setKDCCredentials(KerberosCredential)
+   */
+  KerberosCredential getKDCCredentials() throws AmbariException;
 
   /**
    * Command to invoke against the Ambari backend.

+ 93 - 74
ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java

@@ -30,8 +30,8 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -60,6 +60,9 @@ import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
 import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.metadata.RoleCommandOrder;
+import org.apache.ambari.server.security.SecurePasswordHelper;
+import org.apache.ambari.server.security.encryption.InMemoryCredentialStoreService;
+import org.apache.ambari.server.security.encryption.MasterKeyServiceImpl;
 import org.apache.ambari.server.serveraction.ServerAction;
 import org.apache.ambari.server.serveraction.kerberos.CleanupServerAction;
 import org.apache.ambari.server.serveraction.kerberos.CreateKeytabFilesServerAction;
@@ -126,10 +129,15 @@ public class KerberosHelperImpl implements KerberosHelper {
   private static final Logger LOG = LoggerFactory.getLogger(KerberosHelperImpl.class);
 
   /**
-   * name of the property used to hold the service check identifier value, used when creating and
-   * destroying the (unique) service check identity.
+   * The alias to assign to the KDC administrator credential Keystore item
    */
-  private static final String SERVICE_CHECK_IDENTIFIER = "_kerberos_internal_service_check_identifier";
+  public static final String KDC_ADMINISTRATOR_CREDENTIAL_ALIAS = "kdc_admin";
+
+  /**
+   * The default length of time (in minutes) to store the KDC administrator credentials before
+   * automatically removing them.
+   */
+  private static final long DEFAULT_KDC_ADMINISTRATOR_CREDENTIALS_RETENTION_MINUTES = 90;
 
   /**
    * Regular expression pattern used to parse auth_to_local property specifications into the following
@@ -184,12 +192,29 @@ public class KerberosHelperImpl implements KerberosHelper {
   @Inject
   private KerberosIdentityDataFileWriterFactory kerberosIdentityDataFileWriterFactory;
 
+  @Inject
+  private SecurePasswordHelper securePasswordHelper;
+
   /**
    * Used to get kerberos descriptors associated with the cluster or stack.
    * Currently not available via injection.
    */
   private static ClusterController clusterController = null;
 
+  /**
+   * The secure storage facility to use to store KDC administrator credentials. This implementation
+   * is uses an InMemoryCredentialStoreService to keep the credentials in memory rather than
+   * storing them on disk.
+   */
+  private final InMemoryCredentialStoreService kdcCredentialStoreService;
+
+  /**
+   * Default KerberosHelperImpl constructor
+   */
+  public KerberosHelperImpl() {
+    kdcCredentialStoreService = new InMemoryCredentialStoreService(DEFAULT_KDC_ADMINISTRATOR_CREDENTIALS_RETENTION_MINUTES, TimeUnit.MINUTES, true);
+  }
+
   @Override
   public RequestStageContainer toggleKerberos(Cluster cluster, SecurityType securityType,
                                               RequestStageContainer requestStageContainer,
@@ -344,10 +369,6 @@ public class KerberosHelperImpl implements KerberosHelper {
                                                   RequestStageContainer requestStageContainer)
       throws KerberosOperationException, AmbariException {
     requestStageContainer = handleTestIdentity(cluster, getKerberosDetails(cluster, null), commandParamsStage, requestStageContainer, new DeletePrincipalsAndKeytabsHandler());
-
-    // Clear the Kerberos service check identifier
-    setKerberosServiceCheckIdentifier(cluster, null);
-
     return requestStageContainer;
   }
 
@@ -764,7 +785,7 @@ public class KerberosHelperImpl implements KerberosHelper {
     // add clusterHostInfo config
     Map<String, Set<String>> clusterHostInfo = StageUtils.getClusterHostInfo(cluster);
 
-    if(clusterHostInfo != null) {
+    if (clusterHostInfo != null) {
       Map<String, String> componentHosts = new HashMap<String, String>();
 
       clusterHostInfo = StageUtils.substituteHostIndexes(clusterHostInfo);
@@ -889,6 +910,67 @@ public class KerberosHelperImpl implements KerberosHelper {
     return activeIdentities;
   }
 
+  /**
+   * Sets the KDC administrator credentials.
+   * <p/>
+   * This implementation stores the credentials in a secure CredentialStoreService implementation and
+   * sets a timer to remove the stored credentials after the retention period expires.
+   * <p/>
+   * If existing credentials are stored when setting new credentials, the previously stored data will
+   * be cleared out. Each time credentials are set, a new master key is generated and used to encrypt
+   * the data.
+   *
+   * @param credentials the KDC administrator credentials
+   * @throws AmbariException if an error occurs while storing the credentials
+   */
+  @Override
+  public void setKDCCredentials(KerberosCredential credentials) throws AmbariException {
+    kdcCredentialStoreService.removeCredential(KDC_ADMINISTRATOR_CREDENTIAL_ALIAS);
+
+    if (credentials != null) {
+      String jsonValue = credentials.toJSON();
+
+      if (jsonValue != null) {
+        kdcCredentialStoreService.setMasterKeyService(new MasterKeyServiceImpl(securePasswordHelper.createSecurePassword()));
+        kdcCredentialStoreService.addCredential(KDC_ADMINISTRATOR_CREDENTIAL_ALIAS, jsonValue.toCharArray());
+      }
+    }
+  }
+
+  /**
+   * Removes the previously set KDC administrator credentials.
+   * <p/>
+   * This implementation clears the secure CredentialsStoreService instance, removing the previously
+   * generated master key and credentials data.  The configured timer to enforce retention time is
+   * cleared and set to null.
+   *
+   * @throws AmbariException if an error occurs while removing the credentials
+   * @see KerberosHelper#setKDCCredentials(KerberosCredential)
+   */
+  @Override
+  public void removeKDCCredentials() throws AmbariException {
+    kdcCredentialStoreService.removeCredential(KDC_ADMINISTRATOR_CREDENTIAL_ALIAS);
+  }
+
+  /**
+   * Gets the previously stored KDC administrator credentials.
+   * <p/>
+   * This implementation accesses the secure CredentialStoreService instance to get the data.
+   *
+   * @return a KerberosCredential or null, if the KDC administrator credentials have not be set or
+   * have been removed
+   * @throws AmbariException if an error occurs while retrieving the credentials
+   * @see KerberosHelper#setKDCCredentials(KerberosCredential)
+   */
+  @Override
+  public KerberosCredential getKDCCredentials() throws AmbariException {
+    char[] credentials = kdcCredentialStoreService.getCredential(KDC_ADMINISTRATOR_CREDENTIAL_ALIAS);
+
+    return (credentials == null)
+        ? null
+        : KerberosCredential.fromJSON(new String(credentials));
+  }
+
   /**
    * Validate the KDC admin credentials.
    *
@@ -908,8 +990,8 @@ public class KerberosHelperImpl implements KerberosHelper {
     }
 
     if (kerberosDetails.manageIdentities()) {
-      String credentials = getEncryptedAdministratorCredentials(cluster);
-      if (credentials == null) {
+      KerberosCredential kerberosCredentials = getKDCCredentials();
+      if (kerberosCredentials == null) {
         throw new KerberosMissingAdminCredentialsException(
             "Missing KDC administrator credentials.\n" +
                 "The KDC administrator credentials must be set in session by updating the relevant Cluster resource." +
@@ -926,9 +1008,6 @@ public class KerberosHelperImpl implements KerberosHelper {
         if (operationHandler == null) {
           throw new AmbariException("Failed to get an appropriate Kerberos operation handler.");
         } else {
-          byte[] key = Integer.toHexString(cluster.hashCode()).getBytes();
-          KerberosCredential kerberosCredentials = KerberosCredential.decrypt(credentials, key);
-
           boolean missingCredentials = false;
           try {
             operationHandler.open(kerberosCredentials, kerberosDetails.getDefaultRealm(), kerberosDetails.getKerberosEnvProperties());
@@ -1243,9 +1322,6 @@ public class KerberosHelperImpl implements KerberosHelper {
             }
           });
 
-          // Get or create the unique service check identifier
-          String serviceCheckId = getKerberosServiceCheckIdentifier(cluster, true);
-
           try {
             // Iterate over the hosts in the cluster to find the components installed in each.  For each
             // component (aka service component host - sch) determine the configuration updates and
@@ -1261,9 +1337,6 @@ public class KerberosHelperImpl implements KerberosHelper {
                 // variables within the Kerberos descriptor data
                 Map<String, Map<String, String>> configurations = calculateConfigurations(cluster, hostname, kerberosDescriptorProperties);
 
-                // Set the unique service check identifier
-                configurations.get("").put("service_check_id", serviceCheckId);
-
                 // Add a short date value
                 configurations.get("").put("short_date", new SimpleDateFormat("MMddyy").format(new Date()));
 
@@ -1705,56 +1778,6 @@ public class KerberosHelperImpl implements KerberosHelper {
     return encryptedAdministratorCredentials;
   }
 
-  /**
-   * Using the session data from the relevant Cluster object, gets the previously stored
-   * Kerberos service check identifier value or creates a new one if indicated to do so.
-   * <p/>
-   * This value is used intended to be used by the KerberosHelper to manage uniquely crated
-   * principals for use in service checks.
-   *
-   * @param cluster the relevant Cluster
-   * @return the previously stored Kerberos service check identifier value, or null if
-   * not previously stored
-   */
-  private String getKerberosServiceCheckIdentifier(Cluster cluster, boolean createIfNull) {
-    Map<String, Object> sessionAttributes = cluster.getSessionAttributes();
-    Object value = (sessionAttributes == null) ? null : sessionAttributes.get(SERVICE_CHECK_IDENTIFIER);
-    String serviceCheckIdentifier = (value instanceof String) ? (String) value : null;
-
-    if ((serviceCheckIdentifier == null) && createIfNull) {
-      // Create a new (ideally) unique(ish) identifier
-      Random random = new Random(System.currentTimeMillis());
-      char[] chars = new char[8];
-
-      for (int i = 0; i < 8; i++) {
-        chars[i] = (char) ((int) 'a' + random.nextInt(26));
-      }
-
-      serviceCheckIdentifier = String.valueOf(chars);
-      setKerberosServiceCheckIdentifier(cluster, serviceCheckIdentifier);
-    }
-
-    return serviceCheckIdentifier;
-  }
-
-  /**
-   * Stores the Kerberos service check identifier value into the session data from the
-   * relevant Cluster object.
-   * <p/>
-   * This value is used intended to be used by the KerberosHelper to manage uniquely crated
-   * principals for use in service checks.
-   *
-   * @param cluster the relevant Cluster
-   * @param value   the Kerberos service check identifier to store or null to clear any previously set value
-   */
-  private void setKerberosServiceCheckIdentifier(Cluster cluster, String value) {
-    if (value == null) {
-      cluster.removeSessionAttribute(SERVICE_CHECK_IDENTIFIER);
-    } else {
-      cluster.setSessionAttribute(SERVICE_CHECK_IDENTIFIER, value);
-    }
-  }
-
   /**
    * Given a Collection of ServiceComponentHosts generates a unique list of hosts.
    *
@@ -2432,7 +2455,6 @@ public class KerberosHelperImpl implements KerberosHelper {
 
       if (kerberosDetails.manageIdentities()) {
         commandParameters.put(KerberosServerAction.KDC_TYPE, kerberosDetails.getKdcType().name());
-        commandParameters.put(KerberosServerAction.ADMINISTRATOR_CREDENTIAL, getEncryptedAdministratorCredentials(cluster));
 
         // *****************************************************************
         // Create stage to create principals
@@ -2544,7 +2566,6 @@ public class KerberosHelperImpl implements KerberosHelper {
 
       if (kerberosDetails.manageIdentities()) {
         commandParameters.put(KerberosServerAction.KDC_TYPE, kerberosDetails.getKdcType().name());
-        commandParameters.put(KerberosServerAction.ADMINISTRATOR_CREDENTIAL, getEncryptedAdministratorCredentials(cluster));
 
         // *****************************************************************
         // Create stage to remove principals
@@ -2681,7 +2702,6 @@ public class KerberosHelperImpl implements KerberosHelper {
 
       if (kerberosDetails.manageIdentities()) {
         commandParameters.put(KerberosServerAction.KDC_TYPE, kerberosDetails.getKdcType().name());
-        commandParameters.put(KerberosServerAction.ADMINISTRATOR_CREDENTIAL, getEncryptedAdministratorCredentials(cluster));
 
         // *****************************************************************
         // Create stage to create principals
@@ -2781,7 +2801,6 @@ public class KerberosHelperImpl implements KerberosHelper {
         }
 
         commandParameters.put(KerberosServerAction.KDC_TYPE, kerberosDetails.getKdcType().name());
-        commandParameters.put(KerberosServerAction.ADMINISTRATOR_CREDENTIAL, getEncryptedAdministratorCredentials(cluster));
 
         // *****************************************************************
         // Create stage to create principals

+ 36 - 30
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java

@@ -22,48 +22,54 @@ import org.apache.ambari.server.configuration.Configuration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.Random;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class CredentialProvider {
-  public static final Pattern PASSWORD_ALIAS_PATTERN =
-    Pattern.compile("\\$\\{alias=[\\w\\.]+\\}");
+  public static final Pattern PASSWORD_ALIAS_PATTERN = Pattern.compile("\\$\\{alias=[\\w\\.]+\\}");
 
-  protected char[] chars = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
-    'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
-    'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
-    'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
-    '2', '3', '4', '5', '6', '7', '8', '9'};
+  protected char[] chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g',
+      'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+      'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
+      'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+      '2', '3', '4', '5', '6', '7', '8', '9'};
 
   private CredentialStoreService keystoreService;
   static final Logger LOG = LoggerFactory.getLogger(CredentialProvider.class);
 
   public CredentialProvider(String masterKey, String masterKeyLocation,
-              boolean isMasterKeyPersisted) throws AmbariException {
+                            boolean isMasterKeyPersisted) throws AmbariException {
     MasterKeyService masterKeyService;
     if (masterKey != null) {
       masterKeyService = new MasterKeyServiceImpl(masterKey);
     } else {
-      masterKeyService = new MasterKeyServiceImpl(masterKeyLocation,
-        isMasterKeyPersisted);
+      if (isMasterKeyPersisted) {
+        if ((masterKeyLocation == null) || masterKeyLocation.isEmpty()) {
+          throw new IllegalArgumentException("The master key file location may not be null or empty if the master key is persisted");
+        }
+        masterKeyService = new MasterKeyServiceImpl(new File(masterKeyLocation));
+      } else {
+        masterKeyService = new MasterKeyServiceImpl();
+      }
     }
     if (!masterKeyService.isMasterKeyInitialized()) {
       throw new AmbariException("Master key initialization failed.");
     }
     String storeDir = masterKeyLocation.substring(0,
-      masterKeyLocation.indexOf(Configuration.MASTER_KEY_FILENAME_DEFAULT));
-    this.keystoreService = new CredentialStoreServiceImpl(storeDir);
+        masterKeyLocation.indexOf(Configuration.MASTER_KEY_FILENAME_DEFAULT));
+    this.keystoreService = new FileBasedCredentialStoreService(storeDir);
     this.keystoreService.setMasterKeyService(masterKeyService);
   }
 
   public char[] getPasswordForAlias(String alias) throws AmbariException {
-    if (isAliasString(alias))
+    if (isAliasString(alias)) {
       return keystoreService.getCredential(getAliasFromString(alias));
+    }
     return keystoreService.getCredential(alias);
   }
 
@@ -73,13 +79,14 @@ public class CredentialProvider {
   }
 
   public void addAliasToCredentialStore(String alias, String passwordString)
-    throws AmbariException {
-    if (alias == null || alias.isEmpty())
+      throws AmbariException {
+    if (alias == null || alias.isEmpty()) {
       throw new IllegalArgumentException("Alias cannot be null or empty.");
-    if (passwordString == null || passwordString.isEmpty())
-      throw new IllegalArgumentException("Empty or null password not allowed" +
-        ".");
-    keystoreService.addCredential(alias, passwordString);
+    }
+    if (passwordString == null || passwordString.isEmpty()) {
+      throw new IllegalArgumentException("Empty or null password not allowed.");
+    }
+    keystoreService.addCredential(alias, passwordString.toCharArray());
   }
 
   private String generatePassword(int length) {
@@ -92,15 +99,15 @@ public class CredentialProvider {
   }
 
   public static boolean isAliasString(String aliasStr) {
-    if (aliasStr == null || aliasStr.isEmpty())
+    if (aliasStr == null || aliasStr.isEmpty()) {
       return false;
+    }
     Matcher matcher = PASSWORD_ALIAS_PATTERN.matcher(aliasStr);
     return matcher.matches();
   }
 
   private String getAliasFromString(String strPasswd) {
-    return strPasswd.substring(strPasswd.indexOf("=") + 1,
-      strPasswd.length() - 1);
+    return strPasswd.substring(strPasswd.indexOf("=") + 1, strPasswd.length() - 1);
   }
 
   protected CredentialStoreService getKeystoreService() {
@@ -113,8 +120,8 @@ public class CredentialProvider {
    * args[1] => Alias
    * args[2] => Payload (FilePath for GET/Password for PUT)
    * args[3] => Master Key (Empty)
-   * @param args
    *
+   * @param args
    */
   public static void main(String args[]) {
     if (args != null && args.length > 0) {
@@ -130,15 +137,14 @@ public class CredentialProvider {
         System.exit(1);
       }
       // None - To avoid incorrectly assuming redirection as argument
-      if (args.length > 3 && !args[3].isEmpty() && !args[3].equalsIgnoreCase
-        ("None")) {
+      if (args.length > 3 && !args[3].isEmpty() && !args[3].equalsIgnoreCase("None")) {
         masterKey = args[3];
         LOG.debug("Master key provided as an argument.");
       }
       try {
         credentialProvider = new CredentialProvider(masterKey,
-          configuration.getMasterKeyLocation(),
-          configuration.isMasterKeyPersisted());
+            configuration.getMasterKeyLocation(),
+            configuration.isMasterKeyPersisted());
       } catch (Exception ex) {
         ex.printStackTrace();
         System.exit(1);
@@ -150,7 +156,7 @@ public class CredentialProvider {
           password = args[2];
         }
         if (alias != null && !alias.isEmpty()
-          && password != null && !password.isEmpty()) {
+            && password != null && !password.isEmpty()) {
           try {
             credentialProvider.addAliasToCredentialStore(alias, password);
           } catch (AmbariException e) {
@@ -166,7 +172,7 @@ public class CredentialProvider {
           writeFilePath = args[2];
         }
         if (alias != null && !alias.isEmpty() && writeFilePath != null &&
-          !writeFilePath.isEmpty()) {
+            !writeFilePath.isEmpty()) {
           String passwd = "";
           try {
             char[] retPasswd = credentialProvider.getPasswordForAlias(alias);

+ 34 - 5
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreService.java

@@ -20,10 +20,39 @@ package org.apache.ambari.server.security.encryption;
 
 import org.apache.ambari.server.AmbariException;
 
-import java.security.KeyStore;
-
 public interface CredentialStoreService {
-  public void addCredential(String alias, String key) throws AmbariException;
-  public char[] getCredential(String alias) throws AmbariException;
-  public void setMasterKeyService(MasterKeyService masterService);
+  /**
+   * Adds a new credential to this CredentialStoreService
+   * <p/>
+   * The supplied key will be converted into UTF-8 bytes before being stored.
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @param key   an array of chars containing the credential
+   * @throws AmbariException if an error occurs while storing the new credential
+   */
+  void addCredential(String alias, char[] key) throws AmbariException;
+
+  /**
+   * Retrieves the specified credential from this CredentialStoreService
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @return an array of chars containing the credential
+   * @throws AmbariException if an error occurs while retrieving the new credential
+   */
+  char[] getCredential(String alias) throws AmbariException;
+
+  /**
+   * Removes the specified credential from this CredentialStoreService
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @throws AmbariException if an error occurs while removing the new credential
+   */
+  void removeCredential(String alias) throws AmbariException;
+
+  /**
+   * Sets the MasterKeyService for this CredentialStoreService
+   *
+   * @param masterKeyService the MasterKeyService
+   */
+  void setMasterKeyService(MasterKeyService masterKeyService);
 }

+ 203 - 123
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java

@@ -18,183 +18,263 @@
 package org.apache.ambari.server.security.encryption;
 
 import org.apache.ambari.server.AmbariException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import javax.crypto.spec.SecretKeySpec;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
 import java.security.Key;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
+import java.util.Arrays;
 
-public class CredentialStoreServiceImpl implements CredentialStoreService {
-  private static final String CREDENTIALS_SUFFIX = "credentials.jceks";
-  private static final String DEFAULT_STORE_TYPE = "JCEKS";
-  private MasterKeyService masterService;
-  private String keyStoreDir;
-  private static volatile boolean isCredentialStoreCreated = false;
-  static final Logger LOG = LoggerFactory.getLogger
-    (CredentialStoreServiceImpl.class);
-
-  public CredentialStoreServiceImpl(String keyStoreDir) {
-    this.keyStoreDir = keyStoreDir;
-    final File keyStoreFile = new File(keyStoreDir + File.separator +
-      CREDENTIALS_SUFFIX);
-    if (keyStoreFile.exists()) {
-      isCredentialStoreCreated = true;
+/**
+ * CredentialStoreServiceImpl is an abstract implementation of CredentialStoreService that loads and
+ * stores @{link KeyStore} data. Implementations of this class, provide the input and output streams
+ * used to read and write the data.
+ */
+public abstract class CredentialStoreServiceImpl implements CredentialStoreService {
+  protected static final String DEFAULT_STORE_TYPE = "JCEKS";
+
+  /**
+   * The MasterKeyService containing the key used to encrypt the KeyStore data
+   */
+  private MasterKeyService masterKeyService;
+
+  @Override
+  public void addCredential(String alias, char[] value) throws AmbariException {
+    if ((alias == null) || alias.isEmpty()) {
+      throw new IllegalArgumentException("Alias cannot be null or empty.");
     }
+
+    KeyStore ks = loadCredentialStore();
+    addCredential(ks, alias, value);
+    persistCredentialStore(ks);
   }
 
-  protected KeyStore loadCredentialStore() throws AmbariException {
-    if (masterService == null)
-      throw new AmbariException("Master Key Service is not set for this " +
-        "Credential store.");
+  @Override
+  public char[] getCredential(String alias) throws AmbariException {
+    if (alias == null) {
+      return null;
+    } else {
+      return getCredential(loadCredentialStore(), alias);
+    }
+  }
 
-    final File keyStoreFile = new File(keyStoreDir + File.separator +
-      CREDENTIALS_SUFFIX);
-    LOG.debug("keystoreFile => " + keyStoreFile.getAbsolutePath());
-    if (!isCredentialStoreCreated) {
-      createCredentialStore();
+  @Override
+  public void removeCredential(String alias) throws AmbariException {
+    if ((alias != null) && !alias.isEmpty()) {
+      KeyStore ks = loadCredentialStore();
+      if (ks != null) {
+        try {
+          ks.deleteEntry(alias);
+          persistCredentialStore(ks);
+        } catch (KeyStoreException e) {
+          throw new AmbariException("Failed to delete the KeyStore entry - the key store may not have been initialized", e);
+        }
+      }
     }
-    return getKeystore(keyStoreFile, DEFAULT_STORE_TYPE);
   }
 
   @Override
-  public void addCredential(String alias, String value) throws
-    AmbariException {
-    KeyStore ks = loadCredentialStore();
-    if (ks != null) {
+  public void setMasterKeyService(MasterKeyService masterKeyService) {
+    this.masterKeyService = masterKeyService;
+  }
+
+  /**
+   * Adds a new credential to the supplied KeyStore
+   * <p/>
+   * The supplied key will be converted into UTF-8 bytes before being stored.
+   *
+   * @param keyStore the KeyStore
+   * @param alias    a string declaring the alias (or name) of the credential
+   * @param value    an array of chars containing the credential
+   * @throws AmbariException if an error occurs while storing the new credential
+   */
+  protected void addCredential(KeyStore keyStore, String alias, char[] value) throws AmbariException {
+    if (keyStore != null) {
       try {
-        final Key key = new SecretKeySpec(value.getBytes("UTF8"), "AES");
-        ks.setKeyEntry( alias, key, masterService.getMasterSecret(), null);
-        final File  keyStoreFile = new File(keyStoreDir + File.separator +
-          CREDENTIALS_SUFFIX);
-        writeKeystoreToFile(ks, keyStoreFile);
+        Key key;
+
+        if ((value == null) || (value.length == 0)) {
+          key = null;
+        } else {
+          key = new SecretKeySpec(toBytes(value), "AES");
+        }
+
+        keyStore.setKeyEntry(alias, key, masterKeyService.getMasterSecret(), null);
       } catch (KeyStoreException e) {
-        e.printStackTrace();
-      } catch (NoSuchAlgorithmException e) {
-        e.printStackTrace();
-      } catch (CertificateException e) {
-        e.printStackTrace();
-      } catch (IOException e) {
-        e.printStackTrace();
+        throw new AmbariException("The key store has not been initialized", e);
       }
     }
   }
 
-  @Override
-  public char[] getCredential(String alias) throws AmbariException {
+  /**
+   * Retrieves the specified credential from a KeyStore
+   *
+   * @param keyStore the KeyStore
+   * @param alias    a string declaring the alias (or name) of the credential
+   * @return an array of chars containing the credential
+   * @throws AmbariException if an error occurs while retrieving the new credential
+   */
+  protected char[] getCredential(KeyStore keyStore, String alias) throws AmbariException {
     char[] credential = null;
-    KeyStore ks = loadCredentialStore();
-    if (ks != null && alias != null && !alias.isEmpty()) {
+
+    if (keyStore != null) {
       try {
-        LOG.debug("keystore = " + ks.aliases());
-        Key key = ks.getKey(alias, masterService.getMasterSecret());
-        if (key == null) {
-          throw new AmbariException("Credential not found for alias: " +
-            alias);
+        Key key = keyStore.getKey(alias, masterKeyService.getMasterSecret());
+        if (key != null) {
+          credential = toChars(key.getEncoded());
         }
-        credential = new String(key.getEncoded()).toCharArray();
       } catch (UnrecoverableKeyException e) {
-        e.printStackTrace();
+        throw new AmbariException("The key cannot be recovered (e.g., the given password is wrong)", e);
       } catch (KeyStoreException e) {
-        e.printStackTrace();
+        throw new AmbariException("The key store has not been initialized", e);
       } catch (NoSuchAlgorithmException e) {
-        e.printStackTrace();
+        throw new AmbariException(" if the algorithm for recovering the key cannot be found", e);
       }
     }
+
     return credential;
   }
 
-  public void writeKeystoreToFile(final KeyStore keyStore, final File file)
-    throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
-    final FileOutputStream  out = new FileOutputStream(file);
-    try {
-      keyStore.store(out, masterService.getMasterSecret());
-    }
-    finally {
-      out.close();
-    }
-  }
+  /**
+   * Calls the implementation-specific facility to persist the KeyStore
+   *
+   * @param keyStore the KeyStore to persist
+   * @throws AmbariException if an error occurs while persisting the key store data
+   */
+  protected abstract void persistCredentialStore(KeyStore keyStore) throws AmbariException;
 
-  private synchronized void createCredentialStore() {
-    String filename = keyStoreDir + File.separator + CREDENTIALS_SUFFIX;
-    createKeystore(filename, DEFAULT_STORE_TYPE);
-    isCredentialStoreCreated = true;
-  }
+  /**
+   * Calls the implementation-specific facility to load the KeyStore
+   *
+   * @throws AmbariException if an error occurs while loading the key store data
+   */
+  protected abstract KeyStore loadCredentialStore() throws AmbariException;
+
+  /**
+   * Loads a KeyStore from an InputStream
+   * <p/>
+   * Implementations are expected to call this to load the relevant KeyStore data from the
+   * InputStream of some storage facility.
+   *
+   * @param inputStream  the InputStream to read the data from
+   * @param keyStoreType the type of key store data expected
+   * @return a new KeyStore instance with the loaded data
+   * @throws AmbariException if an error occurs while loading the key store data from the InputStream
+   */
+  protected KeyStore loadKeyStore(InputStream inputStream, String keyStoreType) throws AmbariException {
+    if (masterKeyService == null) {
+      throw new AmbariException("Master Key Service is not set for this Credential store.");
+    }
 
-  private void createKeystore(String filename, String keystoreType) {
-    FileOutputStream out = null;
+    KeyStore keyStore;
     try {
-      out = new FileOutputStream(filename);
-      KeyStore ks = KeyStore.getInstance(keystoreType);
-      ks.load(null, null);
-      ks.store(out, masterService.getMasterSecret());
+      keyStore = KeyStore.getInstance(keyStoreType);
     } catch (KeyStoreException e) {
-      e.printStackTrace();
-    } catch (NoSuchAlgorithmException e) {
-      e.printStackTrace();
+      throw new AmbariException(String.format("No provider supports a key store implementation for the specified type: %s", keyStoreType), e);
+    }
+
+    try {
+      keyStore.load(inputStream, masterKeyService.getMasterSecret());
     } catch (CertificateException e) {
-      e.printStackTrace();
-    } catch (FileNotFoundException e) {
-      e.printStackTrace();
+      throw new AmbariException(String.format("One or more credentials from the key store could not be loaded: %s", e.getLocalizedMessage()), e);
+    } catch (NoSuchAlgorithmException e) {
+      throw new AmbariException(String.format("The algorithm used to check the integrity of the key store cannot be found: %s", e.getLocalizedMessage()), e);
     } catch (IOException e) {
-      e.printStackTrace();
-    } finally {
-      try {
-        out.close();
-      } catch (IOException e) {
-        e.printStackTrace();
+      if (e.getCause() instanceof UnrecoverableKeyException) {
+        throw new AmbariException(String.format("The password used to decrypt the key store is incorrect: %s", e.getLocalizedMessage()), e);
+      } else {
+        throw new AmbariException(String.format("Failed to read the key store: %s", e.getLocalizedMessage()), e);
       }
     }
+
+    return keyStore;
   }
 
-  private KeyStore getKeystore(final File keyStoreFile, String storeType) {
-    KeyStore credStore = null;
+  /**
+   * Writes a KeyStore to an OutputStream
+   * <p/>
+   * Implementations are expected to call this to write the relevant KeyStore data to the
+   * OutputStream of some storage facility.
+   *
+   * @param keyStore     the KeyStore to write
+   * @param outputStream the OutputStream to write the data into
+   * @throws AmbariException if an error occurs while writing the key store data
+   */
+  protected void writeKeyStore(KeyStore keyStore, OutputStream outputStream) throws AmbariException {
+    if (masterKeyService == null) {
+      throw new AmbariException("Master Key Service is not set for this Credential store.");
+    }
+
     try {
-      credStore = loadKeyStore(keyStoreFile, masterService.getMasterSecret(), storeType);
+      keyStore.store(outputStream, masterKeyService.getMasterSecret());
     } catch (CertificateException e) {
-      e.printStackTrace();
-    } catch (KeyStoreException e) {
-      e.printStackTrace();
+      throw new AmbariException(String.format("A credential within in the key store data could not be stored: %s", e.getLocalizedMessage()), e);
     } catch (NoSuchAlgorithmException e) {
-      e.printStackTrace();
+      throw new AmbariException(String.format("The appropriate data integrity algorithm could not be found: %s", e.getLocalizedMessage()), e);
+    } catch (KeyStoreException e) {
+      throw new AmbariException(String.format("The key store has not been initialized: %s", e.getLocalizedMessage()), e);
     } catch (IOException e) {
-      e.printStackTrace();
+      throw new AmbariException(String.format("Failed to write the key store: %s", e.getLocalizedMessage()), e);
     }
-    return credStore;
   }
 
-  private static KeyStore loadKeyStore(final File keyStoreFile,
-          final char[] masterPassword, String storeType)
-    throws CertificateException, IOException,
-    KeyStoreException, NoSuchAlgorithmException {
-    final KeyStore  keyStore = KeyStore.getInstance(storeType);
-    if (keyStoreFile.exists()) {
-      final FileInputStream input   = new FileInputStream(keyStoreFile);
-      try {
-        keyStore.load(input, masterPassword);
-      }
-      finally {
-        input.close();
-      }
-    }
-    else {
-      keyStore.load(null, masterPassword);
-    }
+  /**
+   * Converts an array of characters to an array of bytes by encoding each character into UTF-8 bytes.
+   * <p/>
+   * An attempt is made to clear out sensitive data by filling any buffers with 0's
+   *
+   * @param chars the array of chars to convert
+   * @return an array of bytes, or null if the original array was null
+   */
+  protected byte[] toBytes(char[] chars) {
+    if (chars == null) {
+      return null;
+    } else {
+      CharBuffer charBuffer = CharBuffer.wrap(chars);
+      ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
 
-    return keyStore;
+      byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
+
+      // Clear sensitive data
+      Arrays.fill(charBuffer.array(), '\u0000');
+      Arrays.fill(byteBuffer.array(), (byte) 0);
+
+      return bytes;
+    }
   }
 
-  @Override
-  public void setMasterKeyService(MasterKeyService masterService) {
-    this.masterService = masterService;
+  /**
+   * Converts an array of bytes to an array of character by decoding the bytes using the UTF-8
+   * character set.
+   * <p/>
+   * An attempt is made to clear out sensitive data by filling any buffers with 0's
+   *
+   * @param bytes the array of bytes to convert
+   * @return an array of chars, or null if the original array was null
+   */
+  protected char[] toChars(byte[] bytes) {
+    if (bytes == null) {
+      return null;
+    } else {
+      ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+      CharBuffer charBuffer = Charset.forName("UTF-8").decode(byteBuffer);
+
+      char[] chars = Arrays.copyOfRange(charBuffer.array(), charBuffer.position(), charBuffer.limit());
+
+      // Clear sensitive data
+      Arrays.fill(charBuffer.array(), '\u0000');
+      Arrays.fill(byteBuffer.array(), (byte) 0);
+
+      return chars;
+    }
   }
 }

+ 151 - 0
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/FileBasedCredentialStoreService.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.ambari.server.security.encryption;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.security.KeyStore;
+
+/**
+ * FileBasedCredentialStoreService is a CredentialStoreService implementation that creates and manages
+ * a JCEKS (Java Cryptography Extension KeyStore) file on disk.  The key store and its contents are
+ * encrypted using the key from the supplied {@link MasterKeyService}.
+ * <p/>
+ * Most of the work for this implementation is handled by the {@link CredentialStoreServiceImpl}.
+ * This class handles the details of the storage location and associated input and output streams.
+ */
+public class FileBasedCredentialStoreService extends CredentialStoreServiceImpl {
+  private static final String KEYSTORE_FILENAME = "credentials.jceks";
+  private static final Logger LOG = LoggerFactory.getLogger(FileBasedCredentialStoreService.class);
+
+  /**
+   * The directory to use for storing the key store file
+   */
+  private File keyStoreDir;
+
+  /**
+   * Constructs a new FileBasedCredentialStoreService using the specified key store directory
+   *
+   * @param keyStoreDir a String containing the absolute path to the directory in which to store the key store file
+   */
+  public FileBasedCredentialStoreService(String keyStoreDir) {
+    this(new File(keyStoreDir));
+  }
+
+  /**
+   * Constructs a new FileBasedCredentialStoreService using the specified key store directory
+   *
+   * @param keyStoreDir a File pointing to the directory in which to store the key store file
+   */
+  public FileBasedCredentialStoreService(File keyStoreDir) {
+    if (keyStoreDir == null) {
+      LOG.warn("Writing key store to the current working directory of the running process");
+    } else if (!keyStoreDir.exists()) {
+      LOG.warn("The destination directory does not exist. Failures may occur when writing the key store to disk: {}", keyStoreDir.getAbsolutePath());
+    } else if (!keyStoreDir.isDirectory()) {
+      LOG.warn("The destination does not point to directory. Failures may occur when writing the key store to disk: {}", keyStoreDir.getAbsolutePath());
+    }
+
+    this.keyStoreDir = keyStoreDir;
+  }
+
+  @Override
+  protected void persistCredentialStore(KeyStore keyStore) throws AmbariException {
+    putKeyStore(keyStore, getKeyStoreFile());
+  }
+
+
+  @Override
+  protected KeyStore loadCredentialStore() throws AmbariException {
+    return getKeyStore(getKeyStoreFile(), DEFAULT_STORE_TYPE);
+  }
+
+  /**
+   * Reads the key store data from the specified file. If the file does not exist, a new KeyStore
+   * will be created.
+   *
+   * @param keyStoreFile a File pointing to the key store file
+   * @param keyStoreType the type of key store data to read (or create)
+   * @return the loaded KeyStore
+   * @throws AmbariException           if the Master Key Service is not set
+   * @see CredentialStoreServiceImpl#loadCredentialStore()
+   */
+  private KeyStore getKeyStore(final File keyStoreFile, String keyStoreType) throws AmbariException{
+    KeyStore keyStore;
+    FileInputStream inputStream;
+
+    if (keyStoreFile.exists()) {
+      LOG.debug("Reading key store from {}", keyStoreFile.getAbsolutePath());
+      try {
+        inputStream = new FileInputStream(keyStoreFile);
+      } catch (FileNotFoundException e) {
+        throw new AmbariException(String.format("Failed to open the key store file: %s", e.getLocalizedMessage()), e);
+      }
+    } else {
+      LOG.debug("Key store file not found in {}. Returning new (non-persisted) KeyStore", keyStoreFile.getAbsolutePath());
+      inputStream = null;
+    }
+
+    try {
+      keyStore = loadKeyStore(inputStream, keyStoreType);
+    } finally {
+      IOUtils.closeQuietly(inputStream);
+    }
+
+    return keyStore;
+  }
+
+  /**
+   * Writes the specified KeyStore to a file.
+   *
+   * @param keyStore     the KeyStore to write to a file
+   * @param keyStoreFile the File in which to store the KeyStore data
+   * @throws AmbariException if an error occurs while writing the KeyStore data
+   */
+  private void putKeyStore(KeyStore keyStore, File keyStoreFile) throws AmbariException {
+    LOG.debug("Writing key store to {}", keyStoreFile.getAbsolutePath());
+
+    FileOutputStream outputStream = null;
+
+    try {
+      outputStream = new FileOutputStream(new File(keyStoreDir, KEYSTORE_FILENAME));
+      writeKeyStore(keyStore, outputStream);
+    } catch (FileNotFoundException e) {
+      throw new AmbariException(String.format("Failed to open the key store file: %s", e.getLocalizedMessage()), e);
+    } finally {
+      IOUtils.closeQuietly(outputStream);
+    }
+  }
+
+  /**
+   * Calculates the absolute path to the key store file
+   *
+   * @return a File pointing to the absolute path of the key store file
+   */
+  private File getKeyStoreFile() {
+    return new File(keyStoreDir, KEYSTORE_FILENAME);
+  }
+}

+ 142 - 0
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/InMemoryCredentialStoreService.java

@@ -0,0 +1,142 @@
+/*
+ * 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.security.encryption;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.apache.ambari.server.AmbariException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.KeyStore;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * InMemoryCredentialStoreService is a CredentialStoreService implementation that creates and manages
+ * a JCEKS (Java Cryptography Extension KeyStore) in memory.  The key store and its contents are
+ * encrypted using the key from the supplied {@link MasterKeyService}.
+ * <p/>
+ * This class handles the details of the in-memory storage buffer and associated input and output
+ * streams. Each credential is stored in its own KeyStore that may be be purged upon some
+ * retention timeout - if specified.
+ */
+public class InMemoryCredentialStoreService extends CredentialStoreServiceImpl {
+  private static final Logger LOG = LoggerFactory.getLogger(InMemoryCredentialStoreService.class);
+
+  /**
+   * A cache containing the KeyStore data
+   */
+  private final Cache<String, KeyStore> cache;
+
+  /**
+   * Constructs a new InMemoryCredentialStoreService where credentials have no retention timeout
+   */
+  public InMemoryCredentialStoreService() {
+    this(0, TimeUnit.MINUTES, false);
+  }
+
+  /**
+   * Constructs a new InMemoryCredentialStoreService with a specified credential timeout
+   *
+   * @param retentionDuration the time in some units to keep stored credentials, from the time they are added
+   * @param units             the units for the retention duration (minutes, seconds, etc...)
+   * @param activelyPurge     true to actively purge credentials after the retention time has expired;
+   *                          otherwise false, to passively purge credentials after the retention time has expired
+   */
+  public InMemoryCredentialStoreService(final long retentionDuration, final TimeUnit units, boolean activelyPurge) {
+    CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
+
+    // If the retentionDuration is less the 1, then no retention policy is to be enforced
+    if (retentionDuration > 0) {
+      // If actively purging expired credentials, set up a timer to periodically clean the cache
+      if (activelyPurge) {
+        ThreadFactory threadFactory = new ThreadFactory() {
+          @Override
+          public Thread newThread(Runnable runnable) {
+            Thread t = Executors.defaultThreadFactory().newThread(runnable);
+            if (t != null) {
+              t.setName(String.format("%s active cleanup timer", InMemoryCredentialStoreService.class.getSimpleName()));
+            }
+            return t;
+          }
+        };
+        Runnable runnable = new Runnable() {
+          @Override
+          public void run() {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Cleaning up cache due to retention timeout of {} milliseconds",
+                  units.toMillis(retentionDuration));
+            }
+            cache.cleanUp();
+          }
+        };
+
+        Executors.newSingleThreadScheduledExecutor(threadFactory).schedule(runnable, 1, TimeUnit.MINUTES);
+      }
+
+      builder.expireAfterWrite(retentionDuration, units);
+    }
+
+    cache = builder.build();
+  }
+
+  @Override
+  public void addCredential(String alias, char[] value) throws AmbariException {
+    if ((alias == null) || alias.isEmpty()) {
+      throw new IllegalArgumentException("Alias cannot be null or empty.");
+    }
+
+    KeyStore keyStore = loadKeyStore(null, DEFAULT_STORE_TYPE);
+    addCredential(keyStore, alias, value);
+    cache.put(alias, keyStore);
+  }
+
+  @Override
+  public char[] getCredential(String alias) throws AmbariException {
+    char[] credential = null;
+
+    if ((alias != null) && !alias.isEmpty()) {
+      KeyStore keyStore = cache.getIfPresent(alias);
+      if (keyStore != null) {
+        credential = getCredential(keyStore, alias);
+      }
+    }
+
+    return credential;
+  }
+
+  @Override
+  public void removeCredential(String alias) throws AmbariException {
+    if (alias != null) {
+      cache.invalidate(alias);
+    }
+  }
+
+  @Override
+  protected void persistCredentialStore(KeyStore keyStore) throws AmbariException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  protected KeyStore loadCredentialStore() throws AmbariException {
+    throw new UnsupportedOperationException();
+  }
+}

+ 209 - 172
ambari-server/src/main/java/org/apache/ambari/server/security/encryption/MasterKeyServiceImpl.java

@@ -18,103 +18,62 @@
 
 package org.apache.ambari.server.security.encryption;
 
+import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.commons.net.ntp.TimeStamp;
+
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
-import java.io.PrintWriter;
+import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-import java.util.Random;
 
 public class MasterKeyServiceImpl implements MasterKeyService {
-  private static Log LOG = LogFactory.getLog(MasterKeyServiceImpl.class);
-  private static final String MASTER_PERSISTENCE_TAG = "#1.0# " +
-    TimeStamp.getCurrentTime().toDateString();
-  private char[] master = null;
-  private String MASTER_PASSPHRASE = "masterpassphrase";
-  private AESEncryptor aes = new AESEncryptor(MASTER_PASSPHRASE);
+  private static final Log LOG = LogFactory.getLog(MasterKeyServiceImpl.class);
+  private static final String MASTER_PASSPHRASE = "masterpassphrase";
+  private static final String MASTER_PERSISTENCE_TAG_PREFIX = "#1.0# ";
+  private static final AESEncryptor aes = new AESEncryptor(MASTER_PASSPHRASE);
 
-  // TODO: Create static factory methods vs constructors
+  private char[] master = null;
 
   /**
-   * Test/console friendly construction
-   * @param masterKey
-   * @param masterFileLocation
-   * @param persistMaster
+   * Constructs a new MasterKeyServiceImpl using a master key read from a file.
+   *
+   * @param masterKeyFile the location of the master key file
    */
-  public MasterKeyServiceImpl(String masterKey, String masterFileLocation,
-                              boolean persistMaster) {
-    this.master = masterKey.toCharArray();
-    if (masterFileLocation != null) {
-      if (persistMaster) {
-        LOG.debug("Persisting master key file.");
-        File masterFile = new File(masterFileLocation);
-        if (masterFile.exists()) {
-          LOG.info("Resetting master key before persist.");
-          try {
-            PrintWriter pw = new PrintWriter(masterFile);
-            pw.print("");
-            pw.close();
-          } catch (FileNotFoundException e) {
-            LOG.error("Cannot reset master key file located at: " +
-              masterFileLocation);
-            e.printStackTrace();
-          }
-        }
-        persistMaster(masterFile);
-      }
-    } else {
-      if (persistMaster) {
-        LOG.error("Cannot persist master key without specifying master key " +
-          "location.");
-      }
+  public MasterKeyServiceImpl(File masterKeyFile) {
+    if (masterKeyFile == null) {
+      throw new IllegalArgumentException("Master Key location not provided.");
     }
-  }
 
-  /**
-   * Construction - post creation of the key
-   * @param masterFileLocation
-   * @param isPersisted
-   */
-  public MasterKeyServiceImpl(String masterFileLocation, boolean isPersisted) {
-    if (masterFileLocation == null || masterFileLocation.isEmpty())
-      throw new IllegalArgumentException("Master Key location not provided.");
-    if (isPersisted) {
-      File masterFile = new File(masterFileLocation);
-      if (masterFile.exists()) {
+    if (masterKeyFile.exists()) {
+      if (isMasterKeyFile(masterKeyFile)) {
         try {
-          initializeFromFile(masterFile);
-        } catch (Exception ex) {
-          LOG.error("Cannot intitialize master key from file: " +
-            masterFileLocation + "\n" + ex);
+          initializeFromFile(masterKeyFile);
+        } catch (Exception e) {
+          LOG.error(String.format("Cannot initialize master key from %s: %s", masterKeyFile.getAbsolutePath(), e.getLocalizedMessage()), e);
         }
       } else {
-        LOG.error("Cannot find master key at specified location " +
-          masterFileLocation);
+        LOG.error(String.format("The file at %s is not a master ket file", masterKeyFile.getAbsolutePath()));
       }
     } else {
-      // Master key is not persisted, read from environment.
-      String key = readMasterKey();
-      if (key != null) {
-        this.master = key.toCharArray();
-      } else {
-        LOG.debug("Master key is not provided as a System property or an " +
-          "environment varialble.");
-      }
+      LOG.error(String.format("Cannot open master key file, %s", masterKeyFile.getAbsolutePath()));
     }
   }
 
   /**
-   * Construction for Non-persisted master key
-   * @param masterKey
+   * Constructs a new MasterKeyServiceImpl using the specified master key.
+   *
+   * @param masterKey the master key
    */
   public MasterKeyServiceImpl(String masterKey) {
     if (masterKey != null) {
@@ -125,16 +84,190 @@ public class MasterKeyServiceImpl implements MasterKeyService {
   }
 
   /**
-   * Construction for Non-persisted master key from environment
-   *
+   * Constructs a new MasterKeyServiceImpl using the master key found in the environment.
    */
   public MasterKeyServiceImpl() {
     String key = readMasterKey();
     if (key == null) {
-      throw new IllegalStateException("Cannot read master key from " +
-        "environment.");
-    } else
+      throw new IllegalStateException("Cannot read master key from environment.");
+    } else {
       this.master = key.toCharArray();
+    }
+  }
+
+  public boolean isMasterKeyInitialized() {
+    return this.master != null;
+  }
+
+  @Override
+  public char[] getMasterSecret() {
+    return this.master;
+  }
+
+  public static void main(String args[]) {
+    String masterKey = "ThisissomeSecretPassPhrasse";
+    String masterKeyLocation = "/var/lib/ambari-server/keys/master";
+    boolean persistMasterKey = false;
+    if (args != null && args.length > 0) {
+      masterKey = args[0];
+      if (args.length > 1) {
+        masterKeyLocation = args[1];
+      }
+      if (args.length > 2 && !args[2].isEmpty()) {
+        persistMasterKey = args[2].toLowerCase().equals("true");
+      }
+    }
+
+    if (persistMasterKey && !MasterKeyServiceImpl.initializeMasterKeyFile(new File(masterKeyLocation), masterKey)) {
+      System.exit(1);
+    } else {
+      System.exit(0);
+    }
+  }
+
+  /**
+   * Initializes the master key file.
+   * <p/>
+   * If the specified file already exists, it it tested to see if it is a master key file. If so, it
+   * will be truncated and the new master key will be stored in the new file. If the file appears
+   * to not be a master key file,no changes will be made. The user must manually remove the file if
+   * deemed appropriate.
+   *
+   * @param masterKeyFile the file to write the master key to
+   * @param masterKey     the master key
+   * @return true if the master key was written to the specified file; otherwise false
+   */
+  public static boolean initializeMasterKeyFile(File masterKeyFile, String masterKey) {
+    LOG.debug(String.format("Persisting master key into %s", masterKeyFile.getAbsolutePath()));
+
+    EncryptionResult atom = null;
+
+    if (masterKey != null) {
+      try {
+        atom = aes.encrypt(masterKey);
+      } catch (Exception e) {
+        LOG.error(String.format("Failed to encrypt master key, no changes have been made: %s", e.getLocalizedMessage()), e);
+        return false;
+      }
+    }
+
+    if (masterKeyFile.exists()) {
+      if ((masterKeyFile.length() == 0) || isMasterKeyFile(masterKeyFile)) {
+        LOG.info(String.format("Master key file exists at %s, resetting.", masterKeyFile.getAbsolutePath()));
+        FileChannel fileChannel = null;
+        try {
+          fileChannel = new FileOutputStream(masterKeyFile).getChannel();
+          fileChannel.truncate(0);
+        } catch (FileNotFoundException e) {
+          LOG.error(String.format("Failed to open key file at %s: %s", masterKeyFile.getAbsolutePath(), e.getLocalizedMessage()), e);
+        } catch (IOException e) {
+          LOG.error(String.format("Failed to reset key file at %s: %s", masterKeyFile.getAbsolutePath(), e.getLocalizedMessage()), e);
+        } finally {
+          if (fileChannel != null) {
+            try {
+              fileChannel.close();
+            } catch (IOException e) {
+              // Ignore...
+            }
+          }
+        }
+      } else {
+        LOG.info(String.format("File exists at %s, but may not be a master key file. " +
+            "It must be manually removed before this file location can be used", masterKeyFile.getAbsolutePath()));
+        return false;
+      }
+    }
+
+    if (atom != null) {
+      try {
+        ArrayList<String> lines = new ArrayList<String>();
+        lines.add(MASTER_PERSISTENCE_TAG_PREFIX + TimeStamp.getCurrentTime().toDateString());
+
+        String line = Base64.encodeBase64String((
+            Base64.encodeBase64String(atom.salt) + "::" +
+                Base64.encodeBase64String(atom.iv) + "::" +
+                Base64.encodeBase64String(atom.cipher)).getBytes("UTF8"));
+        lines.add(line);
+        FileUtils.writeLines(masterKeyFile, "UTF8", lines);
+
+        // restrict os permissions to only the user running this process
+        protectAccess(masterKeyFile);
+      } catch (IOException e) {
+        LOG.error(String.format("Failed to persist master key to %s: %s ", masterKeyFile.getAbsolutePath(), e.getLocalizedMessage()), e);
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Determines if the specified file is a "master key" file by checking the file header to see if it
+   * matches an expected value.
+   * <p/>
+   * The "master key" file is expected to have a header (or first line) that starts with "#1.0#". If it,
+   * it is assumed to be a "master key" file, otherwise it is assumed to not be.
+   *
+   * @param file the file to test
+   * @return true if the file is identitified as "master key" file; otherwise false
+   */
+  private static boolean isMasterKeyFile(File file) {
+    FileReader reader = null;
+
+    try {
+      reader = new FileReader(file);
+      char[] buffer = new char[MASTER_PERSISTENCE_TAG_PREFIX.length()];
+      return (reader.read(buffer) == buffer.length) && Arrays.equals(buffer, MASTER_PERSISTENCE_TAG_PREFIX.toCharArray());
+    } catch (Exception e) {
+      // Ignore, assume the file is not a master key file...
+    } finally {
+      if (reader != null) {
+        try {
+          reader.close();
+        } catch (IOException e) {
+          // Ignore...
+        }
+      }
+    }
+
+    return false;
+  }
+
+
+  /**
+   * Ensures that the owner of this process is the only local user account able to read and write to
+   * the specified file or read, write to, and execute the specified directory.
+   *
+   * @param file the file or directory for which to modify access
+   */
+  private static void protectAccess(File file) throws AmbariException {
+    if (file.exists()) {
+      if (!file.setReadable(false, false) || !file.setReadable(true, true)) {
+        String message = String.format("Failed to set %s readable only by current user", file.getAbsolutePath());
+        LOG.warn(message);
+        throw new AmbariException(message);
+      }
+
+      if (!file.setWritable(false, false) || !file.setWritable(true, true)) {
+        String message = String.format("Failed to set %s writable only by current user", file.getAbsolutePath());
+        LOG.warn(message);
+        throw new AmbariException(message);
+      }
+
+      if (file.isDirectory()) {
+        if (!file.setExecutable(false, false) || !file.setExecutable(true, true)) {
+          String message = String.format("Failed to set %s executable by current user", file.getAbsolutePath());
+          LOG.warn(message);
+          throw new AmbariException(message);
+        }
+      } else {
+        if (!file.setExecutable(false, false)) {
+          String message = String.format("Failed to set %s not executable", file.getAbsolutePath());
+          LOG.warn(message);
+          throw new AmbariException(message);
+        }
+      }
+    }
   }
 
   private String readMasterKey() {
@@ -149,8 +282,9 @@ public class MasterKeyServiceImpl implements MasterKeyService {
           if (keyFile.exists()) {
             try {
               initializeFromFile(keyFile);
-              if (this.master != null)
+              if (this.master != null) {
                 key = new String(this.master);
+              }
               FileUtils.deleteQuietly(keyFile);
             } catch (IOException e) {
               LOG.error("Cannot read master key from file: " + keyPath);
@@ -166,42 +300,6 @@ public class MasterKeyServiceImpl implements MasterKeyService {
     return key;
   }
 
-  public boolean isMasterKeyInitialized() {
-    return this.master != null;
-  }
-
-  private EncryptionResult encryptMaster(char[] master) {
-    try {
-      return aes.encrypt(new String(master));
-    } catch (Exception e) {
-      // TODO log failed encryption attempt
-      // need to ensure that we don't persist now
-      e.printStackTrace();
-    }
-    return null;
-  }
-
-  private void persistMaster(File masterFile) {
-    EncryptionResult atom = encryptMaster(master);
-    try {
-      ArrayList<String> lines = new ArrayList<String>();
-      lines.add(MASTER_PERSISTENCE_TAG);
-
-      String line = Base64.encodeBase64String((
-        Base64.encodeBase64String(atom.salt) + "::" +
-          Base64.encodeBase64String(atom.iv) + "::" +
-          Base64.encodeBase64String(atom.cipher)).getBytes("UTF8"));
-      lines.add(line);
-      FileUtils.writeLines(masterFile, "UTF8", lines);
-
-      // restrict os permissions to only the user running this process
-      chmod("600", masterFile);
-    } catch (IOException e) {
-      LOG.error("Failed to persist master. " + e.getLocalizedMessage());
-      e.printStackTrace();
-    }
-  }
-
   private void initializeFromFile(File masterFile) throws Exception {
     try {
       List<String> lines = FileUtils.readLines(masterFile, "UTF8");
@@ -210,8 +308,8 @@ public class MasterKeyServiceImpl implements MasterKeyService {
       String line = new String(Base64.decodeBase64(lines.get(1)));
       String[] parts = line.split("::");
       this.master = new String(aes.decrypt(Base64.decodeBase64(parts[0]),
-        Base64.decodeBase64(parts[1]), Base64.decodeBase64(parts[2])),
-        "UTF8").toCharArray();
+          Base64.decodeBase64(parts[1]), Base64.decodeBase64(parts[2])),
+          "UTF8").toCharArray();
     } catch (IOException e) {
       e.printStackTrace();
       throw e;
@@ -220,65 +318,4 @@ public class MasterKeyServiceImpl implements MasterKeyService {
       throw e;
     }
   }
-
-  @Override
-  public char[] getMasterSecret() {
-    return this.master;
-  }
-
-  private void chmod(String args, File file) throws IOException {
-    if (isUnixEnv()) {
-      //args and file should never be null.
-      if (args == null || file == null)
-        throw new IOException("nullArg");
-      if (!file.exists())
-        throw new IOException("fileNotFound");
-
-      // " +" regular expression for 1 or more spaces
-      final String[] argsString = args.split(" +");
-      List<String> cmdList = new ArrayList<String>();
-      cmdList.add("/bin/chmod");
-      cmdList.addAll(Arrays.asList(argsString));
-      cmdList.add(file.getAbsolutePath());
-      new ProcessBuilder(cmdList).start();
-    }
-  }
-
-  private boolean isUnixEnv() {
-    return (File.separatorChar == '/');
-  }
-
-  private String generateMasterKey() {
-    char[] chars = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
-      'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
-      'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
-      'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
-      '2', '3', '4', '5', '6', '7', '8', '9'};
-
-    StringBuffer sb = new StringBuffer();
-    Random r = new Random();
-    for (int i = 0; i < chars.length; i++) {
-      sb.append(chars[r.nextInt(chars.length)]);
-    }
-    return sb.toString();
-  }
-
-  public static void main(String args[]) {
-    String masterKey = "ThisissomeSecretPassPhrasse";
-    String masterKeyLocation = "/var/lib/ambari-server/keys/master";
-    boolean persistMasterKey = false;
-    if (args != null && args.length > 0) {
-      masterKey = args[0];
-      if (args.length > 1)
-        masterKeyLocation = args[1];
-      if (args.length > 2 && !args[2].isEmpty())
-        persistMasterKey = args[2].toLowerCase().equals("true");
-    }
-    MasterKeyService masterKeyService = new MasterKeyServiceImpl
-      (masterKey, masterKeyLocation, persistMasterKey);
-    if (!masterKeyService.isMasterKeyInitialized()) {
-      System.exit(1);
-    }
-    System.exit(0);
-  }
 }

+ 42 - 12
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosCredential.java

@@ -61,7 +61,7 @@ public class KerberosCredential {
   /**
    * The plaintext password value
    */
-  private String password = null;
+  private char[] password = null;
 
   /**
    * A base64-encoded keytab
@@ -94,7 +94,7 @@ public class KerberosCredential {
     if (map != null) {
       Object attribute;
       String principal;
-      String password;
+      char[] password;
       String keytab;
 
       if (prefix == null) {
@@ -105,13 +105,19 @@ public class KerberosCredential {
       principal = (attribute == null) ? null : attribute.toString();
 
       attribute = map.get(prefix + KEY_NAME_PASSWORD);
-      password = (attribute == null) ? null : attribute.toString();
+      if (attribute instanceof char[]) {
+        password = (char[]) attribute;
+      } else if (attribute instanceof String) {
+        password = ((String) attribute).toCharArray();
+      } else {
+        password = null;
+      }
 
       attribute = map.get(prefix + KEY_NAME_KEYTAB);
       keytab = (attribute == null) ? null : attribute.toString();
 
       if (((principal != null) && !principal.isEmpty()) ||
-          ((password != null) && !password.isEmpty()) ||
+          ((password != null) && (password.length > 0)) ||
           ((keytab != null) && !keytab.isEmpty())) {
         credential = new KerberosCredential(principal, password, keytab);
       }
@@ -141,7 +147,7 @@ public class KerberosCredential {
         Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
         cipher.init(Cipher.DECRYPT_MODE, secretKey);
         byte[] plaintext = cipher.doFinal(Base64.decodeBase64(cipherText));
-        return new Gson().fromJson(new String(plaintext), KerberosCredential.class);
+        return fromJSON(new String(plaintext));
       } catch (NoSuchAlgorithmException e) {
         throw new AmbariException("Failed to decrypt cipher text due to invalid encryption algorithm", e);
       } catch (NoSuchPaddingException e) {
@@ -184,7 +190,7 @@ public class KerberosCredential {
       } catch (NoSuchPaddingException e) {
         throw new AmbariException("Failed to encrypt plaintext due to invalid padding scheme algorithm", e);
       } catch (IllegalBlockSizeException e) {
-        throw new AmbariException("Failed to encrypt plaintext due to invalid key", e);
+        throw new AmbariException("The encryption algorithm is unable to process the input data provided.", e);
       } catch (BadPaddingException e) {
         throw new AmbariException("Failed to encrypt plaintext due to unexpected reasons", e);
       } catch (InvalidKeyException e) {
@@ -206,10 +212,10 @@ public class KerberosCredential {
    * Creates a new KerberosCredential
    *
    * @param principal a String containing the principal name for this Kerberos credential
-   * @param password  a String containing the password for this Kerberos credential
+   * @param password  a char array containing the password for this Kerberos credential
    * @param keytab    a String containing the base64 encoded keytab for this Kerberos credential
    */
-  public KerberosCredential(String principal, String password, String keytab) {
+  public KerberosCredential(String principal, char[] password, String keytab) {
     this.principal = principal;
     this.password = password;
     this.keytab = keytab;
@@ -230,16 +236,16 @@ public class KerberosCredential {
   }
 
   /**
-   * @return a String containing the password for this Kerberos credential
+   * @return a char array containing the password for this Kerberos credential
    */
-  public String getPassword() {
+  public char[] getPassword() {
     return password;
   }
 
   /**
-   * @param password a String containing the password for this Kerberos credential
+   * @param password a char array containing the password for this Kerberos credential
    */
-  public void setPassword(String password) {
+  public void setPassword(char[] password) {
     this.password = password;
   }
 
@@ -270,4 +276,28 @@ public class KerberosCredential {
   public String encrypt(byte[] key) throws AmbariException {
     return encrypt(this, key);
   }
+
+  /**
+   * Returns a JSON representation of this KerberosCredential
+   *
+   * @return a String containing the JSON representation of this KerberosCredential
+   */
+  public String toJSON() {
+    return new Gson().toJson(this);
+  }
+
+  /**
+   * Renders a new KerberosCredential from its JSON representation
+   *
+   * @param json a string containing a JSON representation of a KerberosCredential
+   * @return a new KerberosCredential or null if a new KerberosCredential cannot be created
+   */
+  public static KerberosCredential fromJSON(String json) {
+    try {
+      return ((json == null) || json.isEmpty()) ? null : new Gson().fromJson(json, KerberosCredential.class);
+    }
+    catch(JsonSyntaxException e) {
+      return null;
+    }
+  }
 }

+ 6 - 8
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java

@@ -503,20 +503,18 @@ public abstract class KerberosOperationHandler {
       throw new KerberosAdminAuthenticationException("The administrator credential must not be null");
     }
 
-    String value;
-
     // Ensure the principal is not null or empty
-    value = administratorCredentials.getPrincipal();
-    if ((value == null) || value.isEmpty()) {
+    String principal = administratorCredentials.getPrincipal();
+    if ((principal == null) || principal.isEmpty()) {
       throw new KerberosAdminAuthenticationException("Must specify a principal but it is null or empty");
     }
 
     // Ensure either the password or the keytab value is not null or empty
-    value = administratorCredentials.getPassword();
-    if ((value == null) || value.isEmpty()) {
-      value = administratorCredentials.getKeytab();
+    char[] password  = administratorCredentials.getPassword();
+    if ((password == null) || (password.length == 0)) {
+      String keytab  = administratorCredentials.getKeytab();
 
-      if ((value == null) || value.isEmpty()) {
+      if ((keytab == null) || keytab.isEmpty()) {
         throw new KerberosAdminAuthenticationException("Must specify either a password or a keytab but both are null or empty");
       }
     }

+ 8 - 22
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java

@@ -23,6 +23,7 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.actionmanager.HostRoleStatus;
 import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.agent.ExecutionCommand;
+import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.serveraction.AbstractServerAction;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
@@ -58,12 +59,6 @@ public abstract class KerberosServerAction extends AbstractServerAction {
    */
   public static final String DATA_DIRECTORY = "data_directory";
 
-  /**
-   * A (command parameter) property name used to hold encrypted data representing the KDC
-   * administrator credentials
-   */
-  public static final String ADMINISTRATOR_CREDENTIAL = "kerberos_admin_credential";
-
   /**
    * A (command parameter) property name used to hold the default Kerberos realm value.
    */
@@ -145,6 +140,12 @@ public abstract class KerberosServerAction extends AbstractServerAction {
   @Inject
   private KerberosIdentityDataFileReaderFactory kerberosIdentityDataFileReaderFactory;
 
+  /**
+   * KerberosHelper
+   */
+  @Inject
+  private KerberosHelper kerberosHelper;
+
   /**
    * Given a (command parameter) Map and a property name, attempts to safely retrieve the requested
    * data.
@@ -305,21 +306,6 @@ public abstract class KerberosServerAction extends AbstractServerAction {
     return clusters;
   }
 
-
-  /**
-   * Given a (command parameter) Map, attempts to safely retrieve the "data_directory" property.
-   *
-   * @param commandParameters a Map containing the dictionary of data to interrogate
-   * @return a String indicating the data directory or null (if not found or set)
-   */
-  protected KerberosCredential getAdministratorCredential(Map<String, String> commandParameters) throws AmbariException {
-    Cluster cluster = getCluster();
-
-    // Create the key like we did when we encrypted the data, based on the Cluster objects hashcode.
-    byte[] key = Integer.toHexString(cluster.hashCode()).getBytes();
-    return KerberosCredential.decrypt(getCommandParameterValue(commandParameters, ADMINISTRATOR_CREDENTIAL), key);
-  }
-
   /**
    * Attempts to safely retrieve the "data_directory" property from the this action's relevant
    * command parameters Map.
@@ -355,7 +341,7 @@ public abstract class KerberosServerAction extends AbstractServerAction {
 
     if (commandParameters != null) {
       // Grab the relevant data from this action's command parameters map
-      KerberosCredential administratorCredential = getAdministratorCredential(commandParameters);
+      KerberosCredential administratorCredential = kerberosHelper.getKDCCredentials();
       String defaultRealm = getDefaultRealm(commandParameters);
       KDCType kdcType = getKDCType(commandParameters);
       String dataDirectoryPath = getDataDirectoryPath(commandParameters);

+ 2 - 2
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandler.java

@@ -390,7 +390,7 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
         if((executableKadmin == null) || executableKadmin.isEmpty()) {
           throw new KerberosOperationException("No path for kadmin is available - this KerberosOperationHandler may not have been opened.");
         }
-        String adminPassword = administratorCredentials.getPassword();
+        char[] adminPassword = administratorCredentials.getPassword();
         String adminKeyTab = administratorCredentials.getKeytab();
 
         // Set the kdamin interface to be kadmin
@@ -418,7 +418,7 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
         } else if (adminPassword != null) {
           // Add password for administrative principal
           command.add("-w");
-          command.add(adminPassword);
+          command.add(String.valueOf(adminPassword));
         }
       }
 

+ 1 - 1
ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java

@@ -60,7 +60,7 @@ import com.google.inject.Injector;
 
 @RunWith(PowerMockRunner.class)
 @PrepareForTest({ Configuration.class })
-@PowerMockIgnore( {"javax.management.*"})
+@PowerMockIgnore( {"javax.management.*", "javax.crypto.*"})
 public class ConfigurationTest {
   public TemporaryFolder temp = new TemporaryFolder();
   private Injector injector;

+ 29 - 75
ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java

@@ -258,22 +258,22 @@ public class KerberosHelperTest extends EasyMockSupport {
 
   @Test
   public void testEnableKerberos() throws Exception {
-    testEnableKerberos(new KerberosCredential("principal", "password", "keytab"), "mit-kdc", "true", true, false);
+    testEnableKerberos(new KerberosCredential("principal", "password".toCharArray(), "keytab"), "mit-kdc", "true", true, false);
   }
 
   @Test
   public void testEnableKerberos_ManageIdentitiesFalseKdcNone() throws Exception {
-    testEnableKerberos(new KerberosCredential("principal", "password", "keytab"), "none", "false", true, false);
+    testEnableKerberos(new KerberosCredential("principal", "password".toCharArray(), "keytab"), "none", "false", true, false);
   }
 
   @Test(expected = AmbariException.class)
   public void testEnableKerberos_ManageIdentitiesTrueKdcNone() throws Exception {
-    testEnableKerberos(new KerberosCredential("principal", "password", "keytab"), "none", "true", true, false);
+    testEnableKerberos(new KerberosCredential("principal", "password".toCharArray(), "keytab"), "none", "true", true, false);
   }
 
   @Test(expected = KerberosInvalidConfigurationException.class)
   public void testEnableKerberos_ManageIdentitiesTrueKdcNull() throws Exception {
-    testEnableKerberos(new KerberosCredential("principal", "password", "keytab"), null, "true", true, false);
+    testEnableKerberos(new KerberosCredential("principal", "password".toCharArray(), "keytab"), null, "true", true, false);
   }
 
   @Test(expected = KerberosMissingAdminCredentialsException.class)
@@ -289,7 +289,7 @@ public class KerberosHelperTest extends EasyMockSupport {
   @Test(expected = KerberosMissingAdminCredentialsException.class)
   public void testEnableKerberosInvalidCredentials() throws Exception {
     try {
-      testEnableKerberos(new KerberosCredential("invalid_principal", "password", "keytab"), "mit-kdc", "true", true, false);
+      testEnableKerberos(new KerberosCredential("invalid_principal", "password".toCharArray(), "keytab"), "mit-kdc", "true", true, false);
     } catch (IllegalArgumentException e) {
       Assert.assertTrue(e.getMessage().startsWith("Invalid KDC administrator credentials"));
       throw e;
@@ -298,17 +298,17 @@ public class KerberosHelperTest extends EasyMockSupport {
 
   @Test
   public void testEnableKerberos_GetKerberosDescriptorFromCluster() throws Exception {
-    testEnableKerberos(new KerberosCredential("principal", "password", "keytab"), "mit-kdc", "true", true, false);
+    testEnableKerberos(new KerberosCredential("principal", "password".toCharArray(), "keytab"), "mit-kdc", "true", true, false);
   }
 
   @Test
   public void testEnableKerberos_GetKerberosDescriptorFromStack() throws Exception {
-    testEnableKerberos(new KerberosCredential("principal", "password", "keytab"), "mit-kdc", "true", false, true);
+    testEnableKerberos(new KerberosCredential("principal", "password".toCharArray(), "keytab"), "mit-kdc", "true", false, true);
   }
 
   @Test
   public void testEnsureIdentities() throws Exception {
-    testEnsureIdentities(new KerberosCredential("principal", "password", "keytab"));
+    testEnsureIdentities(new KerberosCredential("principal", "password".toCharArray(), "keytab"));
   }
 
   @Test(expected = KerberosMissingAdminCredentialsException.class)
@@ -324,7 +324,7 @@ public class KerberosHelperTest extends EasyMockSupport {
   @Test(expected = KerberosMissingAdminCredentialsException.class)
   public void testEnsureIdentitiesInvalidCredentials() throws Exception {
     try {
-      testEnsureIdentities(new KerberosCredential("invalid_principal", "password", "keytab"));
+      testEnsureIdentities(new KerberosCredential("invalid_principal", "password".toCharArray(), "keytab"));
     } catch (IllegalArgumentException e) {
       Assert.assertTrue(e.getMessage().startsWith("Invalid KDC administrator credentials"));
       throw e;
@@ -332,7 +332,7 @@ public class KerberosHelperTest extends EasyMockSupport {
   }
   @Test
   public void testDeleteIdentities() throws Exception {
-    testDeleteIdentities(new KerberosCredential("principal", "password", "keytab"));
+    testDeleteIdentities(new KerberosCredential("principal", "password".toCharArray(), "keytab"));
   }
 
   @Test(expected = KerberosMissingAdminCredentialsException.class)
@@ -348,7 +348,7 @@ public class KerberosHelperTest extends EasyMockSupport {
   @Test(expected = KerberosMissingAdminCredentialsException.class)
   public void testDeleteIdentitiesInvalidCredentials() throws Exception {
     try {
-      testDeleteIdentities(new KerberosCredential("invalid_principal", "password", "keytab"));
+      testDeleteIdentities(new KerberosCredential("invalid_principal", "password".toCharArray(), "keytab"));
     } catch (IllegalArgumentException e) {
       Assert.assertTrue(e.getMessage().startsWith("Invalid KDC administrator credentials"));
       throw e;
@@ -382,37 +382,37 @@ public class KerberosHelperTest extends EasyMockSupport {
 
   @Test
   public void testRegenerateKeytabsValidateRequestStageContainer() throws Exception {
-    testRegenerateKeytabs(new KerberosCredential("principal", "password", "keytab"), true, false);
+    testRegenerateKeytabs(new KerberosCredential("principal", "password".toCharArray(), "keytab"), true, false);
   }
 
   @Test
   public void testRegenerateKeytabsValidateSkipInvalidHost() throws Exception {
-    testRegenerateKeytabs(new KerberosCredential("principal", "password", "keytab"), true, true);
+    testRegenerateKeytabs(new KerberosCredential("principal", "password".toCharArray(), "keytab"), true, true);
   }
 
   @Test
   public void testRegenerateKeytabs() throws Exception {
-    testRegenerateKeytabs(new KerberosCredential("principal", "password", "keytab"), false, false);
+    testRegenerateKeytabs(new KerberosCredential("principal", "password".toCharArray(), "keytab"), false, false);
   }
 
   @Test
   public void testDisableKerberos() throws Exception {
-    testDisableKerberos(new KerberosCredential("principal", "password", "keytab"), false, true);
+    testDisableKerberos(new KerberosCredential("principal", "password".toCharArray(), "keytab"), false, true);
   }
 
   @Test
   public void testCreateTestIdentity_ManageIdentitiesDefault() throws Exception {
-    testCreateTestIdentity(new KerberosCredential("principal", "password", "keytab"), null);
+    testCreateTestIdentity(new KerberosCredential("principal", "password".toCharArray(), "keytab"), null);
   }
 
   @Test
   public void testCreateTestIdentity_ManageIdentitiesTrue() throws Exception {
-    testCreateTestIdentity(new KerberosCredential("principal", "password", "keytab"), Boolean.TRUE);
+    testCreateTestIdentity(new KerberosCredential("principal", "password".toCharArray(), "keytab"), Boolean.TRUE);
   }
 
   @Test
   public void testCreateTestIdentity_ManageIdentitiesFalse() throws Exception {
-    testCreateTestIdentity(new KerberosCredential("principal", "password", "keytab"), Boolean.FALSE);
+    testCreateTestIdentity(new KerberosCredential("principal", "password".toCharArray(), "keytab"), Boolean.FALSE);
   }
 
   @Test(expected = KerberosMissingAdminCredentialsException.class)
@@ -432,7 +432,7 @@ public class KerberosHelperTest extends EasyMockSupport {
 
   @Test
   public void testDeleteTestIdentity() throws Exception {
-    testDeleteTestIdentity(new KerberosCredential("principal", "password", "keytab"));
+    testDeleteTestIdentity(new KerberosCredential("principal", "password".toCharArray(), "keytab"));
   }
 
   @Test(expected = IllegalArgumentException.class)
@@ -967,13 +967,6 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(cluster.getCurrentStackVersion())
         .andReturn(new StackId("HDP", "2.2"))
         .anyTimes();
-    expect(cluster.getSessionAttributes()).andReturn(new HashMap<String, Object>() {{
-      if (kerberosCredential != null) {
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PRINCIPAL, kerberosCredential.getPrincipal());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PASSWORD, kerberosCredential.getPassword());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_KEYTAB, kerberosCredential.getKeytab());
-      }
-    }}).anyTimes();
     expect(cluster.getServiceComponentHosts("KERBEROS", "KERBEROS_CLIENT")).andReturn(
         Arrays.asList(schKerberosClient)
     ).once();
@@ -1068,6 +1061,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     // Needed by infrastructure
     metaInfo.init();
 
+    kerberosHelper.setKDCCredentials(kerberosCredential);
     kerberosHelper.toggleKerberos(cluster, SecurityType.KERBEROS, requestStageContainer, null);
 
     verifyAll();
@@ -1188,13 +1182,6 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(cluster.getCurrentStackVersion())
         .andReturn(new StackId("HDP", "2.2"))
         .anyTimes();
-    expect(cluster.getSessionAttributes()).andReturn(new HashMap<String, Object>() {{
-      if (kerberosCredential != null) {
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PRINCIPAL, kerberosCredential.getPrincipal());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PASSWORD, kerberosCredential.getPassword());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_KEYTAB, kerberosCredential.getKeytab());
-      }
-    }}).anyTimes();
 
     final AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class);
     expect(ambariManagementController.findConfigurationTagsWithOverrides(cluster, null))
@@ -1277,6 +1264,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     // Needed by infrastructure
     metaInfo.init();
 
+    kerberosHelper.setKDCCredentials(kerberosCredential);
     kerberosHelper.toggleKerberos(cluster, SecurityType.NONE, requestStageContainer, true);
 
     verifyAll();
@@ -1413,13 +1401,6 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(cluster.getCurrentStackVersion())
         .andReturn(new StackId("HDP", "2.2"))
         .anyTimes();
-    expect(cluster.getSessionAttributes()).andReturn(new HashMap<String, Object>() {{
-      if (kerberosCredential != null) {
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PRINCIPAL, kerberosCredential.getPrincipal());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PASSWORD, kerberosCredential.getPassword());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_KEYTAB, kerberosCredential.getKeytab());
-      }
-    }}).anyTimes();
 
     final AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class);
     expect(ambariManagementController.findConfigurationTagsWithOverrides(cluster, null))
@@ -1502,6 +1483,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     // Needed by infrastructure
     metaInfo.init();
 
+    kerberosHelper.setKDCCredentials(kerberosCredential);
     Assert.assertNotNull(kerberosHelper.executeCustomOperations(cluster, Collections.singletonMap("regenerate_keytabs", "true"), requestStageContainer, true));
 
     verifyAll();
@@ -1910,13 +1892,6 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(cluster.getCurrentStackVersion())
         .andReturn(new StackId("HDP", "2.2"))
         .anyTimes();
-    expect(cluster.getSessionAttributes()).andReturn(new HashMap<String, Object>() {{
-      if (kerberosCredential != null) {
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PRINCIPAL, kerberosCredential.getPrincipal());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PASSWORD, kerberosCredential.getPassword());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_KEYTAB, kerberosCredential.getKeytab());
-      }
-    }}).anyTimes();
 
     final AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class);
     expect(ambariManagementController.findConfigurationTagsWithOverrides(cluster, null))
@@ -2031,6 +2006,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     serviceComponentFilter.put("SERVICE3", Collections.singleton("COMPONENT3"));
     serviceComponentFilter.put("SERVICE1", null);
 
+    kerberosHelper.setKDCCredentials(kerberosCredential);
     kerberosHelper.ensureIdentities(cluster, serviceComponentFilter, identityFilter, null, requestStageContainer, true);
 
     verifyAll();
@@ -2124,13 +2100,6 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(cluster.getCurrentStackVersion())
         .andReturn(new StackId("HDP", "2.2"))
         .anyTimes();
-    expect(cluster.getSessionAttributes()).andReturn(new HashMap<String, Object>() {{
-      if (kerberosCredential != null) {
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PRINCIPAL, kerberosCredential.getPrincipal());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PASSWORD, kerberosCredential.getPassword());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_KEYTAB, kerberosCredential.getKeytab());
-      }
-    }}).anyTimes();
 
     final AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class);
     expect(ambariManagementController.findConfigurationTagsWithOverrides(cluster, null))
@@ -2238,6 +2207,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     serviceComponentFilter.put("SERVICE3", Collections.singleton("COMPONENT3"));
     serviceComponentFilter.put("SERVICE1", null);
 
+    kerberosHelper.setKDCCredentials(kerberosCredential);
     kerberosHelper.deleteIdentities(cluster, serviceComponentFilter, identityFilter, requestStageContainer, true);
 
     verifyAll();
@@ -2266,11 +2236,6 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(krb5ConfConfig.getProperties()).andReturn(krb5ConfProperties).anyTimes();
 
     final Map<String,Object> attributeMap = new HashMap<String, Object>();
-    if (kerberosCredential != null) {
-      attributeMap.put("kerberos_admin/" + KerberosCredential.KEY_NAME_PRINCIPAL, kerberosCredential.getPrincipal());
-      attributeMap.put("kerberos_admin/" + KerberosCredential.KEY_NAME_PASSWORD, kerberosCredential.getPassword());
-      attributeMap.put("kerberos_admin/" + KerberosCredential.KEY_NAME_KEYTAB, kerberosCredential.getKeytab());
-    }
 
     final Cluster cluster = createNiceMock(Cluster.class);
     expect(cluster.getDesiredConfigByType("krb5-conf")).andReturn(krb5ConfConfig).anyTimes();
@@ -2454,14 +2419,12 @@ public class KerberosHelperTest extends EasyMockSupport {
     injector.getInstance(AmbariMetaInfo.class).init();
 
     Map<String, String> commandParamsStage = new HashMap<String, String>();
+    kerberosHelper.setKDCCredentials(kerberosCredential);
     kerberosHelper.createTestIdentity(cluster, commandParamsStage, requestStageContainer);
 
     verifyAll();
 
     if (managingIdentities) {
-      String serviceCheckID = (String) cluster.getSessionAttributes().get("_kerberos_internal_service_check_identifier");
-      Assert.assertNotNull(serviceCheckID);
-
       Assert.assertTrue(commandParamsStage.containsKey("principal_name"));
       Assert.assertEquals("${kerberos-env/service_check_principal_name}@${realm}", commandParamsStage.get("principal_name"));
 
@@ -2560,13 +2523,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(cluster.getCurrentStackVersion())
         .andReturn(new StackId("HDP", "2.2"))
         .anyTimes();
-    expect(cluster.getSessionAttributes()).andReturn(new HashMap<String, Object>() {{
-      if (kerberosCredential != null) {
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PRINCIPAL, kerberosCredential.getPrincipal());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_PASSWORD, kerberosCredential.getPassword());
-        put("kerberos_admin/" + KerberosCredential.KEY_NAME_KEYTAB, kerberosCredential.getKeytab());
-      }
-    }}).anyTimes();
+    expect(cluster.getSessionAttributes()).andReturn(new HashMap<String, Object>()).anyTimes();
 
     final Clusters clusters = injector.getInstance(Clusters.class);
     expect(clusters.getHostsForCluster("c1"))
@@ -2653,14 +2610,11 @@ public class KerberosHelperTest extends EasyMockSupport {
     // Needed by infrastructure
     injector.getInstance(AmbariMetaInfo.class).init();
 
-    String serviceCheckID = "some_random_value";
-
-    cluster.getSessionAttributes().put("_kerberos_internal_service_check_identifier", serviceCheckID);
-
     Map<String, String> commandParamsStage = new HashMap<String, String>();
-    commandParamsStage.put("principal_name", "${cluster-env/smokeuser}_" + serviceCheckID + "@${realm}");
-    commandParamsStage.put("keytab_file", "${keytab_dir}/kerberos.service_check." + serviceCheckID + ".keytab");
+    commandParamsStage.put("principal_name", "${cluster-env/smokeuser}@${realm}");
+    commandParamsStage.put("keytab_file", "${keytab_dir}/kerberos.service_check.keytab");
 
+    kerberosHelper.setKDCCredentials(kerberosCredential);
     kerberosHelper.deleteTestIdentity(cluster, commandParamsStage, requestStageContainer);
 
     verifyAll();

+ 27 - 47
ambari-server/src/test/java/org/apache/ambari/server/security/encryption/CredentialProviderTest.java

@@ -18,43 +18,29 @@
 package org.apache.ambari.server.security.encryption;
 
 import junit.framework.Assert;
-import junit.framework.TestCase;
-import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.configuration.Configuration;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
+
 import java.io.File;
 import java.io.IOException;
 
-public class CredentialProviderTest extends TestCase {
-  private String keystore_dir;
-  private static final Log LOG = LogFactory.getLog
-    (CredentialProviderTest.class);
-
+public class CredentialProviderTest {
   @Rule
   public TemporaryFolder tmpFolder = new TemporaryFolder();
 
-  @Override
-  protected void setUp() throws Exception {
+  @Before
+  public void setUp() throws Exception {
     tmpFolder.create();
-    keystore_dir = tmpFolder.getRoot().getAbsolutePath();
   }
 
-  private void createMasterKey(String dir) {
-    File f = new File(dir + System.getProperty("file" +
-      ".separator") + Configuration.MASTER_KEY_FILENAME_DEFAULT);
-    if (!f.exists()) {
-      try {
-        f.createNewFile();
-      } catch (IOException e) {
-        e.printStackTrace();
-      }
-    }
-    MasterKeyService ms = new MasterKeyServiceImpl("blahblah!",
-      f.getAbsolutePath(), true);
+  private void createMasterKey() throws IOException {
+    File f = tmpFolder.newFile(Configuration.MASTER_KEY_FILENAME_DEFAULT);
+    Assert.assertTrue(MasterKeyServiceImpl.initializeMasterKeyFile(f, "blahblah!"));
+    MasterKeyService ms = new MasterKeyServiceImpl(f);
     if (!ms.isMasterKeyInitialized()) {
       throw new ExceptionInInitializerError("Cannot create master key.");
     }
@@ -62,35 +48,29 @@ public class CredentialProviderTest extends TestCase {
 
   @Test
   public void testInitialization() throws Exception {
-    CredentialProvider cr = null;
-    String msFile = keystore_dir + System.getProperty("file" +
-      ".separator") + Configuration.MASTER_KEY_FILENAME_DEFAULT;
+    CredentialProvider cr;
+    File msFile = tmpFolder.newFile(Configuration.MASTER_KEY_FILENAME_DEFAULT);
     try {
       new CredentialProvider(null, null, true);
-      fail("Expected an exception");
+      Assert.fail("Expected an exception");
     } catch (Throwable t) {
       Assert.assertTrue(t instanceof IllegalArgumentException);
     }
-    try {
-      new CredentialProvider(null, msFile, true);
-      fail("Expected an exception");
-    } catch (Throwable t) {
-      Assert.assertTrue(t instanceof AmbariException);
-    }
     // Without master key persisted
-    cr = new CredentialProvider("blahblah!", msFile, false);
+    cr = new CredentialProvider("blahblah!", msFile.getAbsolutePath(), false);
     Assert.assertNotNull(cr);
     Assert.assertNotNull(cr.getKeystoreService());
     // With master key persisted
-    createMasterKey(keystore_dir);
-    cr = new CredentialProvider(null, msFile, true);
+    msFile.delete();
+    createMasterKey();
+    cr = new CredentialProvider(null, msFile.getAbsolutePath(), true);
     Assert.assertNotNull(cr);
     Assert.assertNotNull(cr.getKeystoreService());
   }
 
   @Test
   public void testIsAliasString() {
-    String test  = "cassablanca";
+    String test = "cassablanca";
     Assert.assertFalse(CredentialProvider.isAliasString(test));
     test = "${}";
     Assert.assertFalse(CredentialProvider.isAliasString(test));
@@ -106,35 +86,35 @@ public class CredentialProviderTest extends TestCase {
 
   @Test
   public void testCredentialStore() throws Exception {
-    String msFile = keystore_dir + System.getProperty("file" +
-      ".separator") + Configuration.MASTER_KEY_FILENAME_DEFAULT;
+    File msFile = tmpFolder.newFile(Configuration.MASTER_KEY_FILENAME_DEFAULT);
+
     // With master key persisted
-    createMasterKey(keystore_dir);
-    CredentialProvider cr = new CredentialProvider(null, msFile, true);
+    createMasterKey();
+    CredentialProvider cr = new CredentialProvider(null, msFile.getAbsolutePath(), true);
     Assert.assertNotNull(cr);
     Assert.assertNotNull(cr.getKeystoreService());
 
     try {
       cr.addAliasToCredentialStore("", "xyz");
-      fail("Expected an exception");
+      Assert.fail("Expected an exception");
     } catch (Throwable t) {
       Assert.assertTrue(t instanceof IllegalArgumentException);
     }
 
     try {
       cr.addAliasToCredentialStore("xyz", null);
-      fail("Expected an exception");
+      Assert.fail("Expected an exception");
     } catch (Throwable t) {
       Assert.assertTrue(t instanceof IllegalArgumentException);
     }
 
     cr.addAliasToCredentialStore("myalias", "mypassword");
     Assert.assertEquals("mypassword", new String(cr.getPasswordForAlias
-      ("myalias")));
+        ("myalias")));
   }
 
-  @Override
-  protected void tearDown() throws Exception {
+  @After
+  public void tearDown() throws Exception {
     tmpFolder.delete();
   }
 }

+ 187 - 57
ambari-server/src/test/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceTest.java

@@ -18,92 +18,222 @@
 package org.apache.ambari.server.security.encryption;
 
 import junit.framework.Assert;
-import junit.framework.TestCase;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
 import java.io.File;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.concurrent.TimeUnit;
 
-public class CredentialStoreServiceTest extends TestCase {
-  private File keystore_dir;
-  private CredentialStoreService credentialStoreService;
-  private static final Log LOG = LogFactory.getLog
-    (CredentialStoreServiceTest.class);
+public class CredentialStoreServiceTest {
 
   @Rule
   public TemporaryFolder tmpFolder = new TemporaryFolder();
 
-  @Override
-  protected void setUp() throws Exception {
+  @Before
+  public void setUp() throws Exception {
     tmpFolder.create();
-    keystore_dir = tmpFolder.newFolder("jcekeystore");
-    LOG.debug("Setting default keystore_dir to " + keystore_dir);
-    credentialStoreService = new
-      CredentialStoreServiceImpl(keystore_dir.getAbsolutePath());
+  }
+
+  @After
+  public void cleanUp() throws Exception {
+    tmpFolder.delete();
   }
 
   @Test
-  public void testAddCredentialToStoreWithPersistMaster() throws Exception {
-    String masterKey = "ThisissomeSecretPassPhrasse";
-    String masterKeyLocation = keystore_dir.getAbsolutePath() + "/master";
-    MasterKeyService masterKeyService = new MasterKeyServiceImpl(masterKey,
-      masterKeyLocation, true);
-    credentialStoreService.setMasterKeyService(masterKeyService);
+  public void testFileBasedCredentialStoreService_AddCredentialToStoreWithPersistMaster() throws Exception {
+    addCredentialToStoreWithPersistMasterTest(new FileBasedCredentialStoreServiceFactory(), new DefaultMasterKeyServiceFactory());
+  }
+
+  @Test
+  public void testFileBasedCredentialStoreService_AddCredentialToStore() throws Exception {
+    addCredentialToStoreTest(new FileBasedCredentialStoreServiceFactory(), new DefaultMasterKeyServiceFactory());
+  }
+
+  @Test
+  public void testFileBasedCredentialStoreService_GetCredential() throws Exception {
+    getCredentialTest(new FileBasedCredentialStoreServiceFactory(), new DefaultMasterKeyServiceFactory());
+  }
+
+  @Test
+  public void testFileBasedCredentialStoreService_RemoveCredential() throws Exception {
+    removeCredentialTest(new FileBasedCredentialStoreServiceFactory(), new DefaultMasterKeyServiceFactory());
+  }
+
+  @Test
+  public void testInMemoryCredentialStoreService_AddCredentialToStoreWithPersistMaster() throws Exception {
+    addCredentialToStoreWithPersistMasterTest(new InMemoryCredentialStoreServiceFactory(), new DefaultMasterKeyServiceFactory());
+  }
+
+  @Test
+  public void testInMemoryCredentialStoreService_AddCredentialToStore() throws Exception {
+    addCredentialToStoreTest(new InMemoryCredentialStoreServiceFactory(), new DefaultMasterKeyServiceFactory());
+  }
+
+  @Test
+  public void testInMemoryCredentialStoreService_GetCredential() throws Exception {
+    getCredentialTest(new InMemoryCredentialStoreServiceFactory(), new DefaultMasterKeyServiceFactory());
+  }
+
+  @Test
+  public void testInMemoryCredentialStoreService_RemoveCredential() throws Exception {
+    removeCredentialTest(new InMemoryCredentialStoreServiceFactory(), new DefaultMasterKeyServiceFactory());
+  }
+
+  @Test
+  public void testInMemoryCredentialStoreService_CredentialExpired() throws Exception {
+    getExpiredCredentialTest(new InMemoryCredentialStoreServiceFactory(), new DefaultMasterKeyServiceFactory());
+  }
+
+  private void addCredentialToStoreWithPersistMasterTest(CredentialStoreServiceFactory credentialStoreServiceFactory,
+                                                         MasterKeyServiceFactory masterKeyServiceFactory) throws Exception {
+    File directory = tmpFolder.getRoot();
+
+    String masterKey = "ThisIsSomeSecretPassPhrase1234";
+    File masterKeyFile = new File(directory, "master");
+
+    MasterKeyService masterKeyService = masterKeyServiceFactory.createPersisted(masterKeyFile, masterKey);
+    CredentialStoreService credentialStoreService = credentialStoreServiceFactory.create(directory, masterKeyService);
+
     String password = "mypassword";
-    credentialStoreService.addCredential("myalias", password);
+    credentialStoreService.addCredential("myalias", password.toCharArray());
     char[] credential = credentialStoreService.getCredential("myalias");
     Assert.assertEquals(password, new String(credential));
+
+    Assert.assertTrue(masterKeyFile.exists());
   }
 
-  @Test
-  public void testAddCredentialToStore() throws Exception {
-    String masterKey = "ThisissomeSecretPassPhrasse";
-    String masterKeyLocation = keystore_dir.getAbsolutePath() + "/master";
-    MasterKeyService masterKeyService = new MasterKeyServiceImpl(masterKey,
-      masterKeyLocation, false);
-    credentialStoreService.setMasterKeyService(masterKeyService);
+  private void addCredentialToStoreTest(CredentialStoreServiceFactory credentialStoreServiceFactory,
+                                        MasterKeyServiceFactory masterKeyServiceFactory) throws Exception {
+    File directory = tmpFolder.getRoot();
+
+    String masterKey = "ThisIsSomeSecretPassPhrase1234";
+    File masterKeyFile = new File(directory, "master");
+
+    MasterKeyService masterKeyService = masterKeyServiceFactory.create(masterKey);
+    CredentialStoreService credentialStoreService = credentialStoreServiceFactory.create(directory, masterKeyService);
+
     String password = "mypassword";
-    credentialStoreService.addCredential("myalias", password);
-    char[] credential = credentialStoreService.getCredential("myalias");
+    credentialStoreService.addCredential("password", password.toCharArray());
+    char[] credential = credentialStoreService.getCredential("password");
     Assert.assertEquals(password, new String(credential));
-    File f = new File(masterKeyLocation);
-    Assert.assertFalse(f.exists());
+
+    credentialStoreService.addCredential("null_password", null);
+    Assert.assertNull(credentialStoreService.getCredential("null_password"));
+
+    credentialStoreService.addCredential("empty_password", new char[0]);
+    Assert.assertNull(credentialStoreService.getCredential("empty_password"));
+
+    Assert.assertFalse(masterKeyFile.exists());
   }
 
-  @Test
-  public void testGetCredential() throws Exception {
-    String masterKey = "ThisissomeSecretPassPhrasse";
-    String masterKeyLocation = keystore_dir.getAbsolutePath() + "/master";
-    MasterKeyService masterKeyService = new MasterKeyServiceImpl(masterKey,
-      masterKeyLocation, false);
-    credentialStoreService.setMasterKeyService(masterKeyService);
+  private void getCredentialTest(CredentialStoreServiceFactory credentialStoreServiceFactory,
+                                 MasterKeyServiceFactory masterKeyServiceFactory) throws Exception {
+    File directory = tmpFolder.getRoot();
+
+    String masterKey = "ThisIsSomeSecretPassPhrase1234";
+
+    MasterKeyService masterKeyService = masterKeyServiceFactory.create(masterKey);
+    CredentialStoreService credentialStoreService = credentialStoreServiceFactory.create(directory, masterKeyService);
+
     Assert.assertNull(credentialStoreService.getCredential(""));
     Assert.assertNull(credentialStoreService.getCredential(null));
+
     String password = "mypassword";
-    credentialStoreService.addCredential("myalias", password);
+    credentialStoreService.addCredential("myalias", password.toCharArray());
     char[] credential = credentialStoreService.getCredential("myalias");
     Assert.assertEquals(password, new String(credential));
+
+    Assert.assertNull(credentialStoreService.getCredential("does_not_exist"));
   }
 
-  @Test
-  public void testAliasParsing() throws Exception {
-    String strPasswd = "${alias=ambari.password}";
-    Pattern PASSWORD_ALIAS_PATTERN = Pattern.compile
-      ("\\$\\{alias=[\\w\\.]+\\}");
-    Matcher matcher = PASSWORD_ALIAS_PATTERN.matcher(strPasswd);
-    Assert.assertTrue(matcher.matches());
-    Assert.assertEquals("ambari.password", strPasswd.substring(strPasswd
-      .indexOf("=")
-      + 1, strPasswd.length() - 1));
-  }
-
-  @Override
-  protected void tearDown() throws Exception {
-    tmpFolder.delete();
+  private void getExpiredCredentialTest(CredentialStoreServiceFactory credentialStoreServiceFactory,
+                                 MasterKeyServiceFactory masterKeyServiceFactory) throws Exception {
+    File directory = tmpFolder.getRoot();
+
+    String masterKey = "ThisIsSomeSecretPassPhrase1234";
+
+    MasterKeyService masterKeyService = masterKeyServiceFactory.create(masterKey);
+    CredentialStoreService credentialStoreService = credentialStoreServiceFactory.create(directory, masterKeyService);
+
+    String password = "mypassword";
+    credentialStoreService.addCredential("myalias", password.toCharArray());
+    Assert.assertEquals(password, new String(credentialStoreService.getCredential("myalias")));
+
+    Thread.sleep(250);
+    Assert.assertEquals(password, new String(credentialStoreService.getCredential("myalias")));
+
+    Thread.sleep(550);
+    Assert.assertNull(password, credentialStoreService.getCredential("myalias"));
+
   }
+
+  private void removeCredentialTest(CredentialStoreServiceFactory credentialStoreServiceFactory,
+                                    MasterKeyServiceFactory masterKeyServiceFactory) throws Exception {
+    File directory = tmpFolder.getRoot();
+
+    String masterKey = "ThisIsSomeSecretPassPhrase1234";
+
+    MasterKeyService masterKeyService = masterKeyServiceFactory.create(masterKey);
+    CredentialStoreService credentialStoreService = credentialStoreServiceFactory.create(directory, masterKeyService);
+
+    String password = "mypassword";
+    credentialStoreService.addCredential("myalias", password.toCharArray());
+
+    char[] credential = credentialStoreService.getCredential("myalias");
+    Assert.assertEquals(password, new String(credential));
+
+    credentialStoreService = credentialStoreServiceFactory.create(directory, masterKeyService);
+    credentialStoreService.setMasterKeyService(masterKeyService);
+
+    credentialStoreService.removeCredential("myalias");
+    Assert.assertNull(credentialStoreService.getCredential("myalias"));
+
+    credentialStoreService.removeCredential("does_not_exist");
+  }
+
+  private interface CredentialStoreServiceFactory {
+    CredentialStoreService create(File directory, MasterKeyService masterKeyService);
+  }
+
+  private class FileBasedCredentialStoreServiceFactory implements CredentialStoreServiceFactory {
+    @Override
+    public CredentialStoreService create(File directory, MasterKeyService masterKeyService) {
+      CredentialStoreService credentialStoreService = new FileBasedCredentialStoreService(directory.getAbsolutePath());
+      credentialStoreService.setMasterKeyService(masterKeyService);
+      return credentialStoreService;
+    }
+  }
+
+  private class InMemoryCredentialStoreServiceFactory implements CredentialStoreServiceFactory {
+    @Override
+    public CredentialStoreService create(File directory, MasterKeyService masterKeyService) {
+      CredentialStoreService credentialStoreService = new InMemoryCredentialStoreService(500, TimeUnit.MILLISECONDS, true);
+      credentialStoreService.setMasterKeyService(masterKeyService);
+      return credentialStoreService;
+    }
+  }
+
+  private interface MasterKeyServiceFactory {
+    MasterKeyService create(String masterKey);
+
+    MasterKeyService createPersisted(File masterKeyFile, String masterKey);
+  }
+
+  private class DefaultMasterKeyServiceFactory implements MasterKeyServiceFactory {
+
+    @Override
+    public MasterKeyService create(String masterKey) {
+      return new MasterKeyServiceImpl(masterKey);
+    }
+
+    @Override
+    public MasterKeyService createPersisted(File masterKeyFile, String masterKey) {
+      MasterKeyServiceImpl.initializeMasterKeyFile(masterKeyFile, masterKey);
+      return new MasterKeyServiceImpl(masterKeyFile);
+    }
+  }
+
 }

+ 46 - 23
ambari-server/src/test/java/org/apache/ambari/server/security/encryption/MasterKeyServiceTest.java

@@ -6,9 +6,9 @@
  * 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
- *
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
  * 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.
@@ -29,9 +29,15 @@ import org.junit.runner.RunWith;
 import org.powermock.core.classloader.annotations.PowerMockIgnore;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
+
 import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
+
 import static org.easymock.EasyMock.expect;
 import static org.powermock.api.easymock.PowerMock.mockStatic;
 import static org.powermock.api.easymock.PowerMock.replayAll;
@@ -39,13 +45,13 @@ import static org.powermock.api.easymock.PowerMock.verifyAll;
 
 @RunWith(PowerMockRunner.class)
 @PowerMockIgnore({"javax.crypto.*", "org.apache.log4j.*"})
-@PrepareForTest({ MasterKeyServiceImpl.class })
+@PrepareForTest({MasterKeyServiceImpl.class})
 public class MasterKeyServiceTest extends TestCase {
   @Rule
   public TemporaryFolder tmpFolder = new TemporaryFolder();
   private String fileDir;
   private static final Log LOG = LogFactory.getLog
-    (MasterKeyServiceTest.class);
+      (MasterKeyServiceTest.class);
 
   @Override
   protected void setUp() throws Exception {
@@ -55,18 +61,35 @@ public class MasterKeyServiceTest extends TestCase {
   }
 
   @Test
-  public void testInitiliazeMasterKey() throws Exception {
-    MasterKeyService ms = new MasterKeyServiceImpl("ThisisSomePassPhrase",
-      fileDir + File.separator + "master", true);
+  public void testInitializeMasterKey() throws Exception {
+    File masterKeyFile = new File(fileDir, "master");
+    Assert.assertTrue(MasterKeyServiceImpl.initializeMasterKeyFile(masterKeyFile, "ThisisSomePassPhrase"));
+
+    MasterKeyService ms = new MasterKeyServiceImpl(masterKeyFile);
     Assert.assertTrue(ms.isMasterKeyInitialized());
-    File f = new File(fileDir + File.separator + "master");
-    Assert.assertTrue(f.exists());
+
+    Assert.assertTrue(masterKeyFile.exists());
+
+    // Make sure the created file is readable and writable only by the process owner.
+    Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(Paths.get(masterKeyFile.getAbsolutePath()));
+    Assert.assertNotNull(permissions);
+    Assert.assertEquals(2, permissions.size());
+    Assert.assertTrue(permissions.contains(PosixFilePermission.OWNER_READ));
+    Assert.assertTrue(permissions.contains(PosixFilePermission.OWNER_WRITE));
+    Assert.assertFalse(permissions.contains(PosixFilePermission.OWNER_EXECUTE));
+    Assert.assertFalse(permissions.contains(PosixFilePermission.GROUP_READ));
+    Assert.assertFalse(permissions.contains(PosixFilePermission.GROUP_WRITE));
+    Assert.assertFalse(permissions.contains(PosixFilePermission.GROUP_EXECUTE));
+    Assert.assertFalse(permissions.contains(PosixFilePermission.OTHERS_READ));
+    Assert.assertFalse(permissions.contains(PosixFilePermission.OTHERS_WRITE));
+    Assert.assertFalse(permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
+
     // Re-initialize master from file
-    MasterKeyService ms1 = new MasterKeyServiceImpl(fileDir + File.separator
-      + "master", true);
+    MasterKeyService ms1 = new MasterKeyServiceImpl(masterKeyFile);
     Assert.assertTrue(ms1.isMasterKeyInitialized());
-    Assert.assertEquals("ThisisSomePassPhrase", new String(ms1.getMasterSecret
-      ()));
+
+    Assert.assertEquals("ThisisSomePassPhrase", new String(ms1.getMasterSecret()));
+    Assert.assertEquals(new String(ms.getMasterSecret()), new String(ms1.getMasterSecret()));
   }
 
   @Test
@@ -81,20 +104,21 @@ public class MasterKeyServiceTest extends TestCase {
     Assert.assertTrue(ms.isMasterKeyInitialized());
     Assert.assertNotNull(ms.getMasterSecret());
     Assert.assertEquals("ThisisSomePassPhrase",
-      new String(ms.getMasterSecret()));
+        new String(ms.getMasterSecret()));
   }
 
   @Test
   public void testReadFromEnvAsPath() throws Exception {
     // Create a master key
-    MasterKeyService ms = new MasterKeyServiceImpl("ThisisSomePassPhrase",
-      fileDir + File.separator + "master", true);
+    File masterKeyFile = new File(fileDir, "master");
+    Assert.assertTrue(MasterKeyServiceImpl.initializeMasterKeyFile(masterKeyFile, "ThisisSomePassPhrase"));
+
+    MasterKeyService ms = new MasterKeyServiceImpl(masterKeyFile);
     Assert.assertTrue(ms.isMasterKeyInitialized());
-    File f = new File(fileDir + File.separator + "master");
-    Assert.assertTrue(f.exists());
+    Assert.assertTrue(masterKeyFile.exists());
 
     Map<String, String> mapRet = new HashMap<String, String>();
-    mapRet.put(Configuration.MASTER_KEY_LOCATION, f.getAbsolutePath());
+    mapRet.put(Configuration.MASTER_KEY_LOCATION, masterKeyFile.getAbsolutePath());
     mockStatic(System.class);
     expect(System.getenv()).andReturn(mapRet);
     replayAll();
@@ -102,9 +126,8 @@ public class MasterKeyServiceTest extends TestCase {
     verifyAll();
     Assert.assertTrue(ms.isMasterKeyInitialized());
     Assert.assertNotNull(ms.getMasterSecret());
-    Assert.assertEquals("ThisisSomePassPhrase",
-      new String(ms.getMasterSecret()));
-    Assert.assertFalse(f.exists());
+    Assert.assertEquals("ThisisSomePassPhrase", new String(ms.getMasterSecret()));
+    Assert.assertFalse(masterKeyFile.exists());
   }
 
   @Override

+ 9 - 9
ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java

@@ -54,7 +54,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest
   @Test(expected = KerberosKDCConnectionException.class)
   public void testOpenExceptionLdapUrlNotProvided() throws Exception {
     KerberosOperationHandler handler = new ADKerberosOperationHandler();
-    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null);
     Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
       {
         put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN);
@@ -67,7 +67,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest
   @Test(expected = KerberosLDAPContainerException.class)
   public void testOpenExceptionPrincipalContainerDnNotProvided() throws Exception {
     KerberosOperationHandler handler = new ADKerberosOperationHandler();
-    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null);
     Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
       {
         put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
@@ -92,7 +92,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest
 
   @Test(expected = KerberosAdminAuthenticationException.class)
   public void testTestAdministratorCredentialsIncorrectAdminPassword() throws Exception {
-    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, "wrong", null);
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, "wrong".toCharArray(), null);
     Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
       {
         put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
@@ -120,7 +120,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest
 
   @Test(expected = KerberosAdminAuthenticationException.class)
   public void testTestAdministratorCredentialsIncorrectAdminPrincipal() throws Exception {
-    KerberosCredential kc = new KerberosCredential("wrong", DEFAULT_ADMIN_PASSWORD, null);
+    KerberosCredential kc = new KerberosCredential("wrong", DEFAULT_ADMIN_PASSWORD.toCharArray(), null);
     Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
       {
         put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
@@ -148,7 +148,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest
 
   @Test(expected = KerberosKDCConnectionException.class)
   public void testTestAdministratorCredentialsKDCConnectionException() throws Exception {
-    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null);
     Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
       {
         put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, "invalid");
@@ -177,7 +177,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest
 
   @Test
   public void testTestAdministratorCredentialsSuccess() throws Exception {
-    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null);
     Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
       {
         put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
@@ -229,7 +229,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest
 
   @Test
   public void testProcessCreateTemplateDefault() throws Exception {
-    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null);
     Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
       {
         put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
@@ -338,7 +338,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest
 
   @Test
   public void testProcessCreateTemplateCustom() throws Exception {
-    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null);
     Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
       {
         put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
@@ -475,7 +475,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest
       containerDN = DEFAULT_PRINCIPAL_CONTAINER_DN;
     }
 
-    KerberosCredential credentials = new KerberosCredential(principal, password, null);
+    KerberosCredential credentials = new KerberosCredential(principal, password.toCharArray(), null);
     Map<String, String> kerberosEnvMap = new HashMap<String, String>();
 
     kerberosEnvMap.put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, ldapUrl);

+ 3 - 3
ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosCredentialTest.java

@@ -58,7 +58,7 @@ public class KerberosCredentialTest {
     kerberosCredential = KerberosCredential.fromMap(attributes, "kerberos_admin/");
     Assert.assertNotNull(kerberosCredential);
     Assert.assertEquals("admin/admin@FOOBAR.COM", kerberosCredential.getPrincipal());
-    Assert.assertEquals("t0p_s3cr3t", kerberosCredential.getPassword());
+    Assert.assertEquals("t0p_s3cr3t", String.valueOf(kerberosCredential.getPassword()));
     Assert.assertNull(kerberosCredential.getKeytab());
 
     // Test with a prefix that does not resolve to any existing keys
@@ -73,7 +73,7 @@ public class KerberosCredentialTest {
     String cipherText;
     KerberosCredential decryptedCredential;
 
-    credential = new KerberosCredential("admin/admin@FOOBAR.COM", "t0p_s3cr3t", null);
+    credential = new KerberosCredential("admin/admin@FOOBAR.COM", "t0p_s3cr3t".toCharArray(), null);
     cipherText = credential.encrypt(key);
     Assert.assertNotNull(cipherText);
 
@@ -81,7 +81,7 @@ public class KerberosCredentialTest {
     decryptedCredential = KerberosCredential.decrypt(cipherText, key);
     Assert.assertNotNull(decryptedCredential);
     Assert.assertEquals(credential.getPrincipal(), decryptedCredential.getPrincipal());
-    Assert.assertEquals(credential.getPassword(), decryptedCredential.getPassword());
+    Assert.assertEquals(String.valueOf(credential.getPassword()), String.valueOf(decryptedCredential.getPassword()));
     Assert.assertEquals(credential.getKeytab(), decryptedCredential.getKeytab());
 
     // Test an invalid key

+ 4 - 4
ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerTest.java

@@ -267,7 +267,7 @@ public abstract class KerberosOperationHandlerTest extends EasyMockSupport {
   public void testAdminCredentialsNullPrincipal() throws KerberosOperationException {
     KerberosOperationHandler handler = createHandler();
 
-    KerberosCredential credentials = new KerberosCredential(null, "password", null);
+    KerberosCredential credentials = new KerberosCredential(null, "password".toCharArray(), null);
     handler.setAdministratorCredentials(credentials);
   }
 
@@ -275,7 +275,7 @@ public abstract class KerberosOperationHandlerTest extends EasyMockSupport {
   public void testAdminCredentialsEmptyPrincipal() throws KerberosOperationException {
     KerberosOperationHandler handler = createHandler();
 
-    KerberosCredential credentials = new KerberosCredential("", "password", null);
+    KerberosCredential credentials = new KerberosCredential("", "password".toCharArray(), null);
     handler.setAdministratorCredentials(credentials);
   }
 
@@ -291,7 +291,7 @@ public abstract class KerberosOperationHandlerTest extends EasyMockSupport {
   public void testAdminCredentialsEmptyCredential1() throws KerberosOperationException {
     KerberosOperationHandler handler = createHandler();
 
-    KerberosCredential credentials = new KerberosCredential("principal", "", null);
+    KerberosCredential credentials = new KerberosCredential("principal", "".toCharArray(), null);
     handler.setAdministratorCredentials(credentials);
   }
 
@@ -383,7 +383,7 @@ public abstract class KerberosOperationHandlerTest extends EasyMockSupport {
       }
     };
 
-    handler.open(new KerberosCredential("admin/admin", "hadoop", null), "EXAMPLE.COM", null);
+    handler.open(new KerberosCredential("admin/admin", "hadoop".toCharArray(), null), "EXAMPLE.COM", null);
     return handler;
   }
 }

+ 20 - 12
ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerActionTest.java

@@ -29,6 +29,7 @@ import org.apache.ambari.server.actionmanager.HostRoleCommand;
 import org.apache.ambari.server.actionmanager.HostRoleStatus;
 import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.agent.ExecutionCommand;
+import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.stack.OsFamily;
@@ -42,10 +43,10 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
+import static org.easymock.EasyMock.*;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
-import static org.easymock.EasyMock.createNiceMock;
 
 public class KerberosServerActionTest {
 
@@ -68,6 +69,7 @@ public class KerberosServerActionTest {
 
       @Override
       protected void configure() {
+        bind(KerberosHelper.class).toInstance(createNiceMock(KerberosHelper.class));
         bind(KerberosServerAction.class).toInstance(new KerberosServerAction() {
 
           @Override
@@ -118,9 +120,6 @@ public class KerberosServerActionTest {
     commandParams.put(KerberosServerAction.DATA_DIRECTORY, temporaryDirectory.getAbsolutePath());
     commandParams.put(KerberosServerAction.DEFAULT_REALM, "REALM.COM");
     commandParams.put(KerberosServerAction.KDC_TYPE, KDCType.MIT_KDC.toString());
-    commandParams.put(KerberosServerAction.ADMINISTRATOR_CREDENTIAL,
-        new KerberosCredential("principal", "password", "keytab")
-            .encrypt(Integer.toHexString(cluster.hashCode()).getBytes()));
 
     when(mockExecutionCommand.getCommandParams()).thenReturn(commandParams);
     when(mockExecutionCommand.getClusterName()).thenReturn("c1");
@@ -183,6 +182,13 @@ public class KerberosServerActionTest {
 
   @Test
   public void testProcessIdentitiesSuccess() throws Exception {
+    KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
+    expect(kerberosHelper.getKDCCredentials())
+        .andReturn(new KerberosCredential("principal", "password".toCharArray(), null))
+        .anyTimes();
+
+    replay(kerberosHelper);
+
     ConcurrentMap<String, Object> sharedMap = new ConcurrentHashMap<String, Object>();
     CommandReport report = action.processIdentities(sharedMap);
     Assert.assertNotNull(report);
@@ -192,24 +198,26 @@ public class KerberosServerActionTest {
       Assert.assertEquals(entry.getValue(),
           entry.getKey().replace("_HOST", "hostName").replace("_REALM", "REALM.COM"));
     }
+
+    verify(kerberosHelper);
   }
 
   @Test
   public void testProcessIdentitiesFail() throws Exception {
+    KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
+    expect(kerberosHelper.getKDCCredentials())
+        .andReturn(new KerberosCredential("principal", "password".toCharArray(), null))
+        .anyTimes();
+
+    replay(kerberosHelper);
+
     ConcurrentMap<String, Object> sharedMap = new ConcurrentHashMap<String, Object>();
     sharedMap.put("FAIL", "true");
 
     CommandReport report = action.processIdentities(sharedMap);
     Assert.assertNotNull(report);
     Assert.assertEquals(HostRoleStatus.FAILED.toString(), report.getStatus());
-  }
 
-  @Test
-  public void testGetAdministrativeCredentials() throws AmbariException {
-    KerberosCredential credentials = action.getAdministratorCredential(commandParams);
-    Assert.assertNotNull(credentials);
-    Assert.assertEquals("principal", credentials.getPrincipal());
-    Assert.assertEquals("password", credentials.getPassword());
-    Assert.assertEquals("keytab", credentials.getKeytab());
+    verify(kerberosHelper);
   }
 }

+ 12 - 12
ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandlerTest.java

@@ -83,7 +83,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
   @Test
   public void testSetPrincipalPasswordExceptions() throws Exception {
     MITKerberosOperationHandler handler = injector.getInstance(MITKerberosOperationHandler.class);
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
 
     try {
       handler.setPrincipalPassword(DEFAULT_ADMIN_PRINCIPAL, null);
@@ -139,7 +139,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
 
     replay(handler, result1, result2);
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
     handler.createPrincipal(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, false);
 
     verify(handler, result1, result2);
@@ -150,7 +150,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
   @Test
   public void testCreateServicePrincipal_Exceptions() throws Exception {
     MITKerberosOperationHandler handler = new MITKerberosOperationHandler();
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
 
     try {
       handler.createPrincipal(DEFAULT_ADMIN_PRINCIPAL, null, false);
@@ -209,7 +209,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -242,7 +242,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -275,7 +275,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -308,7 +308,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -341,7 +341,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -374,7 +374,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -407,7 +407,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
     Assert.assertFalse(handler.testAdministratorCredentials());
     handler.close();
   }
@@ -460,7 +460,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, KERBEROS_ENV_MAP);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD.toCharArray(), null), DEFAULT_REALM, KERBEROS_ENV_MAP);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -485,7 +485,7 @@ public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTes
       realm = DEFAULT_REALM;
     }
 
-    KerberosCredential credentials = new KerberosCredential(principal, password, null);
+    KerberosCredential credentials = new KerberosCredential(principal, password.toCharArray(), null);
 
     handler.open(credentials, realm, KERBEROS_ENV_MAP);
     handler.testAdministratorCredentials();