Browse Source

AMBARI-9170. Principal creation for Active Directory accounts should be configurable (rlevas)

Robert Levas 10 năm trước cách đây
mục cha
commit
01fa8eb03b
18 tập tin đã thay đổi với 630 bổ sung229 xóa
  1. 20 3
      ambari-server/src/main/java/org/apache/ambari/server/actionmanager/Stage.java
  2. 16 51
      ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
  3. 1 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
  4. 35 0
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/AbstractServerAction.java
  5. 218 70
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java
  6. 4 20
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java
  7. 1 26
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
  8. 21 2
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandler.java
  9. 3 3
      ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
  10. 21 0
      ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/configuration/kerberos-env.xml
  11. 1 1
      ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionDBAccessorImpl.java
  12. 1 1
      ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionScheduler.java
  13. 1 1
      ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
  14. 2 2
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/ServerActionExecutorTest.java
  15. 270 28
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java
  16. 4 3
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandlerTest.java
  17. 2 9
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerActionTest.java
  18. 9 9
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandlerTest.java

+ 20 - 3
ambari-server/src/main/java/org/apache/ambari/server/actionmanager/Stage.java

@@ -328,6 +328,8 @@ public class Stage {
    *                      empty or null if no data is relevant
    * @param commandDetail a String declaring a descriptive name to pass to the action - null or an
    *                      empty string indicates no value is to be set
+   * @param configTags    a Map of configuration tags to set for this command - if null, no
+   *                      configurations will be available for the command
    * @param timeout       an Integer declaring the timeout for this action - if null, a default
    * @param retryAllowed   indicates whether retry after failure is allowed
    */
@@ -335,11 +337,12 @@ public class Stage {
                                                   String clusterName, ServiceComponentHostServerActionEvent event,
                                                   @Nullable Map<String, String> commandParams,
                                                   @Nullable String commandDetail,
+                                                  @Nullable Map<String, Map<String,String>> configTags,
                                                   @Nullable Integer timeout,
                                                   boolean retryAllowed) {
 
-    addServerActionCommand(actionName, role, command,
-        clusterName, StageUtils.getHostName(), event, commandParams, commandDetail, timeout, retryAllowed);
+    addServerActionCommand(actionName, role, command, clusterName, StageUtils.getHostName(), event,
+        commandParams, commandDetail, configTags, timeout, retryAllowed);
   }
 
   /**
@@ -363,14 +366,17 @@ public class Stage {
    *                      empty or null if no data is relevant
    * @param commandDetail a String declaring a descriptive name to pass to the action - null or an
    *                      empty string indicates no value is to be set
+   * @param configTags    a Map of configuration tags to set for this command - if null, no
+   *                      configurations will be available for the command
    * @param timeout       an Integer declaring the timeout for this action - if null, a default
-   * @param retryAllowed   indicates whether retry after failure is allowed
+   * @param retryAllowed  indicates whether retry after failure is allowed
    */
   public synchronized void addServerActionCommand(String actionName, Role role, RoleCommand command,
                                                   String clusterName, String hostName,
                                                   ServiceComponentHostServerActionEvent event,
                                                   @Nullable Map<String, String> commandParams,
                                                   @Nullable String commandDetail,
+                                                  @Nullable Map<String, Map<String,String>> configTags,
                                                   @Nullable Integer timeout, boolean retryAllowed) {
     ExecutionCommandWrapper commandWrapper =
         addGenericExecutionCommand(clusterName, hostName, role, command, event, retryAllowed);
@@ -386,6 +392,17 @@ public class Stage {
     }
     cmd.setCommandParams(cmdParams);
 
+    Map<String, Map<String, String>> configurations = new TreeMap<String, Map<String, String>>();
+    cmd.setConfigurations(configurations);
+
+    Map<String, Map<String, Map<String, String>>> configurationAttributes = new TreeMap<String, Map<String, Map<String, String>>>();
+    cmd.setConfigurationAttributes(configurationAttributes);
+
+    if (configTags == null) {
+      configTags = new TreeMap<String, Map<String, String>>();
+    }
+    cmd.setConfigurationTags(configTags);
+
     Map<String, String> roleParams = new HashMap<String, String>();
     roleParams.put(ServerAction.ACTION_NAME, actionName);
     cmd.setRoleParams(roleParams);

+ 16 - 51
ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java

@@ -219,18 +219,12 @@ public class KerberosHelper {
         kdcType = KDCType.MIT_KDC;
       }
 
-      KDCDetails kdcDetails = new KDCDetails(
-          kdcType,
-          (kerberosEnvProperties == null) ? null : kerberosEnvProperties.get("ldap_url"),
-          (kerberosEnvProperties == null) ? null : kerberosEnvProperties.get("container_dn")
-      );
-
       if ("true".equalsIgnoreCase(securityEnabled)) {
         LOG.info("Configuring Kerberos for realm {} on cluster, {}", defaultRealm, cluster.getClusterName());
-        requestStageContainer = handle(cluster, kerberosDescriptor, defaultRealm, kdcDetails, requestStageContainer, enableKerberosHandler);
+        requestStageContainer = handle(cluster, kerberosDescriptor, defaultRealm, kdcType, kerberosEnvProperties, requestStageContainer, enableKerberosHandler);
       } else if ("false".equalsIgnoreCase(securityEnabled)) {
         LOG.info("Disabling Kerberos from cluster, {}", cluster.getClusterName());
-        requestStageContainer = handle(cluster, kerberosDescriptor, defaultRealm, kdcDetails, requestStageContainer, disableKerberosHandler);
+        requestStageContainer = handle(cluster, kerberosDescriptor, defaultRealm, kdcType, kerberosEnvProperties, requestStageContainer, disableKerberosHandler);
       } else {
         String message = String.format("Invalid value for `security_enabled` property of cluster-env: %s", securityEnabled);
         LOG.error(message);
@@ -253,7 +247,8 @@ public class KerberosHelper {
    * @param cluster               the relevant Cluster
    * @param kerberosDescriptor    the (derived) KerberosDescriptor
    * @param realm                 the default Kerberos realm for the Cluster
-   * @param kdcDetails            a KDCDetails containing information about relevant KDC
+   * @param kdcType               a KDCType declaring the type of the relevant KDC
+   * @param kerberosEnvProperties a MAp of key/value pairs from the kerberos-env configuration
    * @param requestStageContainer a RequestStageContainer to place generated stages, if needed -
    *                              if null a new RequestStageContainer will be created.
    * @return the updated or a new RequestStageContainer containing the stages that need to be
@@ -263,8 +258,8 @@ public class KerberosHelper {
   @Transactional
   private RequestStageContainer handle(Cluster cluster,
                                        KerberosDescriptor kerberosDescriptor,
-                                       String realm, KDCDetails kdcDetails,
-                                       RequestStageContainer requestStageContainer,
+                                       String realm, KDCType kdcType,
+                                       Map<String, String> kerberosEnvProperties, RequestStageContainer requestStageContainer,
                                        Handler handler) throws AmbariException {
 
     Map<String, Service> services = cluster.getServices();
@@ -425,7 +420,7 @@ public class KerberosHelper {
                       "}"
               );
             } else {
-              KerberosOperationHandler operationHandler = kerberosOperationHandlerFactory.getKerberosOperationHandler(kdcDetails.getKdcType());
+              KerberosOperationHandler operationHandler = kerberosOperationHandlerFactory.getKerberosOperationHandler(kdcType);
 
               if (operationHandler == null) {
                 throw new AmbariException("Failed to get an appropriate Kerberos operation handler.");
@@ -434,7 +429,7 @@ public class KerberosHelper {
                 KerberosCredential kerberosCredentials = KerberosCredential.decrypt(credentials, key);
 
                 try {
-                  operationHandler.open(kerberosCredentials, realm, kdcDetails.getLdapUrl(), kdcDetails.getPrincipalContainerDn());
+                  operationHandler.open(kerberosCredentials, realm, kerberosEnvProperties);
                   if (!operationHandler.testAdministratorCredentials()) {
                     throw new IllegalArgumentException(
                         "Invalid KDC administrator credentials.\n" +
@@ -526,7 +521,7 @@ public class KerberosHelper {
 
         // Use the handler implementation to setup the relevant stages.
         int lastStageId = handler.createStages(cluster, hosts, kerberosConfigurations,
-            clusterHostInfoJson, hostParamsJson, event, roleCommandOrder, realm, kdcDetails,
+            clusterHostInfoJson, hostParamsJson, event, roleCommandOrder, realm, kdcType,
             dataDirectory, requestStageContainer, serviceComponentHostsToProcess);
 
         // Add the cleanup stage...
@@ -892,7 +887,7 @@ public class KerberosHelper {
                                         Class<? extends ServerAction> actionClass,
                                         ServiceComponentHostServerActionEvent event,
                                         Map<String, String> commandParameters, String commandDetail,
-                                        Integer timeout) {
+                                        Integer timeout) throws AmbariException {
 
     Stage stage = createNewStage(id, cluster, requestId, requestContext, clusterHostInfo, commandParams, hostParams);
     stage.addServerActionCommand(actionClass.getName(),
@@ -902,6 +897,7 @@ public class KerberosHelper {
         event,
         commandParameters,
         commandDetail,
+        ambariManagementController.findConfigurationTagsWithOverrides(cluster, null),
         timeout, false);
 
     return stage;
@@ -1017,7 +1013,7 @@ public class KerberosHelper {
      * @param event                  a ServiceComponentHostServerActionEvent to pass to any created tasks
      * @param roleCommandOrder       the RoleCommandOrder to use to generate the RoleGraph for any newly created Stages
      * @param realm                  a String declaring the cluster's Kerberos realm
-     * @param kdcDetails             a KDCDetails containing the information about the relevant KDC
+     * @param kdcType                a KDCType declaring the type of the relevant KDC
      * @param dataDirectory          a File pointing to the (temporary) data directory
      * @param requestStageContainer  a RequestStageContainer to store the new stages in, if null a
      *                               new RequestStageContainer will be created
@@ -1030,7 +1026,7 @@ public class KerberosHelper {
                      String clusterHostInfo, String hostParams,
                      ServiceComponentHostServerActionEvent event,
                      RoleCommandOrder roleCommandOrder,
-                     String realm, KDCDetails kdcDetails, File dataDirectory,
+                     String realm, KDCType kdcType, File dataDirectory,
                      RequestStageContainer requestStageContainer,
                      List<ServiceComponentHost> serviceComponentHosts)
         throws AmbariException;
@@ -1083,7 +1079,7 @@ public class KerberosHelper {
                             Map<String, Map<String, String>> kerberosConfigurations,
                             String clusterHostInfoJson, String hostParamsJson,
                             ServiceComponentHostServerActionEvent event,
-                            RoleCommandOrder roleCommandOrder, String realm, KDCDetails kdcDetails,
+                            RoleCommandOrder roleCommandOrder, String realm, KDCType kdcType,
                             File dataDirectory, RequestStageContainer requestStageContainer,
                             List<ServiceComponentHost> serviceComponentHosts)
         throws AmbariException {
@@ -1145,9 +1141,7 @@ public class KerberosHelper {
       Map<String, String> commandParameters = new HashMap<String, String>();
       commandParameters.put(KerberosServerAction.DATA_DIRECTORY, dataDirectory.getAbsolutePath());
       commandParameters.put(KerberosServerAction.DEFAULT_REALM, realm);
-      commandParameters.put(KerberosServerAction.KDC_TYPE, kdcDetails.getKdcType().name());
-      commandParameters.put(KerberosServerAction.KDC_LDAP_URL, kdcDetails.getLdapUrl());
-      commandParameters.put(KerberosServerAction.KDC_PRINCIPAL_CONTAINER_DN, kdcDetails.getPrincipalContainerDn());
+      commandParameters.put(KerberosServerAction.KDC_TYPE, kdcType.name());
       commandParameters.put(KerberosServerAction.ADMINISTRATOR_CREDENTIAL, getEncryptedAdministratorCredentials(cluster));
 
       // *****************************************************************
@@ -1284,7 +1278,7 @@ public class KerberosHelper {
                             Map<String, Map<String, String>> kerberosConfigurations,
                             String clusterHostInfoJson, String hostParamsJson,
                             ServiceComponentHostServerActionEvent event,
-                            RoleCommandOrder roleCommandOrder, String realm, KDCDetails kdcDetails,
+                            RoleCommandOrder roleCommandOrder, String realm, KDCType kdcType,
                             File dataDirectory, RequestStageContainer requestStageContainer,
                             List<ServiceComponentHost> serviceComponentHosts) {
       // TODO (rlevas): If there are principals, keytabs, and configurations to process, setup the following sages:
@@ -1295,33 +1289,4 @@ public class KerberosHelper {
       return -1;
     }
   }
-
-
-  /**
-   * KDCDetails is a helper class to hold the details of the relevant KDC so they may be passed
-   * around more easily.
-   */
-  private static class KDCDetails {
-    private final KDCType kdcType;
-    private final String ldapUrl;
-    private final String principalContainerDn;
-
-    public KDCDetails(KDCType kdcType, String ldapUrl, String principalContainerDn) {
-      this.kdcType = kdcType;
-      this.ldapUrl = ldapUrl;
-      this.principalContainerDn = principalContainerDn;
-    }
-
-    public KDCType getKdcType() {
-      return kdcType;
-    }
-
-    public String getLdapUrl() {
-      return ldapUrl;
-    }
-
-    public String getPrincipalContainerDn() {
-      return principalContainerDn;
-    }
-  }
 }

+ 1 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java

@@ -768,6 +768,7 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
         new ServiceComponentHostServerActionEvent(StageUtils.getHostName(), System.currentTimeMillis()),
         commandParams,
         itemDetail,
+        null,
         Integer.valueOf(1200),
         allowRtery);
 

+ 35 - 0
ambari-server/src/main/java/org/apache/ambari/server/serveraction/AbstractServerAction.java

@@ -135,4 +135,39 @@ public abstract class AbstractServerAction implements ServerAction {
     }
   }
 
+  /**
+   * Attempts to safely retrieve a property with the specified name from the this action's relevant
+   * command parameters Map.
+   *
+   * @param propertyName a String declaring the name of the item from commandParameters to retrieve
+   * @return the value of the requested property, or null if not found or set
+   */
+  protected String getCommandParameterValue(String propertyName) {
+    Map<String, String> commandParameters = getCommandParameters();
+    return (commandParameters == null) ? null : commandParameters.get(propertyName);
+  }
+
+  /**
+   * Returns the configurations value from the ExecutionCommand
+   * <p/>
+   * The returned map should be assumed to be read-only.
+   *
+   * @return the (assumed read-only) configurations value from the ExecutionCommand
+   */
+  protected Map<String, Map<String, String>> getConfigurations() {
+    return (executionCommand == null) ? Collections.<String, Map<String, String>>emptyMap() : executionCommand.getConfigurations();
+  }
+
+  /**
+   * Returns the requested configuration Map from the ExecutionCommand
+   * <p/>
+   * The returned map should be assumed to be read-only.
+   *
+   * @param configurationName a String indicating the name of the configuration data to retrieve
+   * @return the (assumed read-only) configuration Map from the ExecutionCommand, or null if not available
+   */
+  protected Map<String, String> getConfiguration(String configurationName) {
+    return getConfigurations().get(configurationName);
+  }
+
 }

+ 218 - 70
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java

@@ -19,16 +19,31 @@
 package org.apache.ambari.server.serveraction.kerberos;
 
 
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import org.apache.ambari.server.utils.StageUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
 
 import javax.naming.*;
 import javax.naming.directory.*;
 import javax.naming.ldap.Control;
 import javax.naming.ldap.InitialLdapContext;
 import javax.naming.ldap.LdapContext;
+import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Implementation of <code>KerberosOperationHandler</code> to created principal in Active Directory
@@ -37,46 +52,76 @@ public class ADKerberosOperationHandler extends KerberosOperationHandler {
 
   private static Log LOG = LogFactory.getLog(ADKerberosOperationHandler.class);
 
+  /**
+   * Regular expression to parse the different principal formats:
+   * primary/instance@REALM
+   * primary@REALM
+   * primary/instance
+   * primary
+   */
+  private static Pattern PATTERN_PRINCIPAL = Pattern.compile("^(([^ /@]+)(?:/([^ /@]+))?)(?:@(.+)?)?$");
+
   private static final String LDAP_CONTEXT_FACTORY_CLASS = "com.sun.jndi.ldap.LdapCtxFactory";
 
-  private String ldapUrl;
-  private String principalContainerDn;
+  public final static String KERBEROS_ENV_LDAP_URL = "ldap_url";
+  public final static String KERBEROS_ENV_PRINCIPAL_CONTAINER_DN = "container_dn";
+  public final static String KERBEROS_ENV_CREATE_ATTRIBUTES_TEMPLATE = "create_attributes_template";
 
-  private static final int ONE_LEVEL_SCOPE = SearchControls.ONELEVEL_SCOPE;
-  private static final String LDAP_ATUH_MECH_SIMPLE = "simple";
+  /**
+   * A String containing the URL for the LDAP interface for the relevant Active Directory
+   */
+  private String ldapUrl = null;
 
-  private LdapContext ldapContext;
-  private SearchControls searchControls;
+  /**
+   * A String containing the DN of the container to create new account in
+   */
+  private String principalContainerDn = null;
 
   /**
-   * Prepares and creates resources to be used by this KerberosOperationHandler.
-   * This method in this class would always throw <code>KerberosOperationException</code> reporting
-   * ldapUrl is not provided.
-   * Use <code>open(KerberosCredential administratorCredentials, String defaultRealm,
-   * String ldapUrl, String principalContainerDn)</code> for successful operation.
+   * A String containing the Velocity template to use to generate the JSON structure declaring the
+   * attributes to use to create new Active Directory accounts.
    * <p/>
-   * It is expected that this KerberosOperationHandler will not be used before this call.
-   *
-   * @param administratorCredentials a KerberosCredential containing the administrative credentials
-   *                                 for the relevant KDC
-   * @param realm                    a String declaring the  Kerberos realm (or domain)
+   * If this value is null, a default template will be used.
    */
-  @Override
-  public void open(KerberosCredential administratorCredentials, String realm)
-      throws KerberosOperationException {
-    open(administratorCredentials, realm, null, null);
-  }
+  private String createTemplate = null;
+
+  /**
+   * The relevant LDAP context, created upon opening this KerberosOperationHandler
+   */
+  private LdapContext ldapContext = null;
+
+  /**
+   * The relevant SearchControls, created upon opening this KerberosOperationHandler
+   */
+  private SearchControls searchControls = null;
+
+  /**
+   * VelocityEngine used to process the "create principal template" that is expected to generate
+   * a JSON structure declaring the attributes of the Active Directory account
+   */
+  private VelocityEngine velocityEngine = null;
+
+  /**
+   * The Gson instance to use to convert the template-generated JSON structure to a Map of attribute
+   * names to values.
+   */
+  private Gson gson = new Gson();
 
   /**
    * Prepares and creates resources to be used by this KerberosOperationHandler
    * <p/>
    * It is expected that this KerberosOperationHandler will not be used before this call.
+   * <p/>
+   * It is expected that the kerberosConfiguration Map has the following properties:
+   * <ul>
+   * <li>ldap_url - ldapUrl of ldap back end where principals would be created</li>
+   * <li>container_dn - DN of the container in ldap back end where principals would be created</li>
+   * </il>
    *
    * @param administratorCredentials a KerberosCredential containing the administrative credentials
    *                                 for the relevant KDC
    * @param realm                    a String declaring the default Kerberos realm (or domain)
-   * @param ldapUrl                  ldapUrl of ldap back end where principals would be created
-   * @param principalContainerDn     DN of the container in ldap back end where principals would be created
+   * @param kerberosConfiguration    a Map of key/value pairs containing data from the kerberos-env configuration set
    * @throws KerberosKDCConnectionException       if a connection to the KDC cannot be made
    * @throws KerberosAdminAuthenticationException if the administrator credentials fail to authenticate
    * @throws KerberosRealmException               if the realm does not map to a KDC
@@ -84,34 +129,45 @@ public class ADKerberosOperationHandler extends KerberosOperationHandler {
    */
   @Override
   public void open(KerberosCredential administratorCredentials, String realm,
-                   String ldapUrl, String principalContainerDn)
-      throws KerberosOperationException {
+                   Map<String, String> kerberosConfiguration) throws KerberosOperationException {
 
     if (isOpen()) {
       close();
     }
 
     if (administratorCredentials == null) {
-      throw new KerberosAdminAuthenticationException("administrator Credential not provided");
+      throw new KerberosAdminAuthenticationException("administrator credentials not provided");
     }
     if (realm == null) {
       throw new KerberosRealmException("realm not provided");
     }
-    if (ldapUrl == null) {
+    if (kerberosConfiguration == null) {
+      throw new KerberosRealmException("kerberos-env configuration may not be null");
+    }
+
+    this.ldapUrl = kerberosConfiguration.get(KERBEROS_ENV_LDAP_URL);
+    if (this.ldapUrl == null) {
       throw new KerberosKDCConnectionException("ldapUrl not provided");
     }
-    if (principalContainerDn == null) {
+
+    this.principalContainerDn = kerberosConfiguration.get(KERBEROS_ENV_PRINCIPAL_CONTAINER_DN);
+    if (this.principalContainerDn == null) {
       throw new KerberosLDAPContainerException("principalContainerDn not provided");
     }
 
     setAdministratorCredentials(administratorCredentials);
     setDefaultRealm(realm);
 
-    this.ldapUrl = ldapUrl;
-    this.principalContainerDn = principalContainerDn;
     this.ldapContext = createLdapContext();
     this.searchControls = createSearchControls();
 
+    this.createTemplate = kerberosConfiguration.get(KERBEROS_ENV_CREATE_ATTRIBUTES_TEMPLATE);
+
+    this.velocityEngine = new VelocityEngine();
+    this.velocityEngine.init();
+
+    this.gson = new Gson();
+
     setOpen(true);
   }
 
@@ -122,11 +178,18 @@ public class ADKerberosOperationHandler extends KerberosOperationHandler {
    */
   @Override
   public void close() throws KerberosOperationException {
-    if (ldapContext != null) {
+    this.searchControls = null;
+    this.velocityEngine = null;
+
+    this.gson = null;
+
+    if (this.ldapContext != null) {
       try {
-        ldapContext.close();
+        this.ldapContext.close();
       } catch (NamingException e) {
         throw new KerberosOperationException("Unexpected error", e);
+      } finally {
+        this.ldapContext = null;
       }
     }
 
@@ -199,52 +262,75 @@ public class ADKerberosOperationHandler extends KerberosOperationHandler {
     }
 
     // TODO: (rlevas) pass components and realm in separately (AMBARI-9122)
-    String realm = getDefaultRealm();
-    int atIndex = principal.indexOf("@");
-    if (atIndex >= 0) {
-      realm = principal.substring(atIndex + 1);
-      principal = principal.substring(0, atIndex);
+    String realm = null;
+    String principal_primary = null;
+    String principal_instance = null;
+
+    Matcher matcher = PATTERN_PRINCIPAL.matcher(principal);
+    if (matcher.matches()) {
+      principal = matcher.group(1);
+      principal_primary = matcher.group(2);
+      principal_instance = matcher.group(3);
+      realm = matcher.group(4);
     }
 
-    if (realm == null) {
-      realm = "";
+    if ((realm == null) || realm.isEmpty()) {
+      realm = getDefaultRealm();
     }
 
-    Attributes attributes = new BasicAttributes();
-
-    Attribute objectClass = new BasicAttribute("objectClass");
-    objectClass.add("user");
-    attributes.put(objectClass);
+    Map<String, Object> context = new HashMap<String, Object>();
+    context.put("principal", principal);
+    context.put("principal_primary", principal_primary);
+    context.put("principal_instance", principal_instance);
+    context.put("realm", realm);
+    context.put("realm_lowercase", (realm == null) ? null : realm.toLowerCase());
+    context.put("password", password);
+    context.put("is_service", service);
+    context.put("container_dn", this.principalContainerDn);
 
-    Attribute cn = new BasicAttribute("cn");
-    cn.add(principal);
-    attributes.put(cn);
+    Map<String, Object> data = processCreateTemplate(context);
 
-    Attribute upn = new BasicAttribute("userPrincipalName");
-    upn.add(String.format("%s@%s", principal, realm.toLowerCase()));
-    attributes.put(upn);
-
-    if (service) {
-      Attribute spn = new BasicAttribute("servicePrincipalName");
-      spn.add(principal);
-      attributes.put(spn);
+    Attributes attributes = new BasicAttributes();
+    String cn = null;
+
+    if (data != null) {
+      for (Map.Entry<String, Object> entry : data.entrySet()) {
+        String key = entry.getKey();
+        Object value = entry.getValue();
+
+        if ("unicodePwd".equals(key)) {
+          if (value instanceof String) {
+            Attribute passwordAttr = new BasicAttribute("unicodePwd");  // password
+            try {
+              passwordAttr.add(((String) value).getBytes("UTF-16LE"));
+            } catch (UnsupportedEncodingException ue) {
+              throw new KerberosOperationException("Can not encode password with UTF-16LE", ue);
+            }
+            attributes.put(passwordAttr);
+          }
+        } else {
+          Attribute attribute = new BasicAttribute(key);
+          if (value instanceof Collection) {
+            for (Object object : (Collection) value) {
+              attribute.add(object);
+            }
+          } else {
+            attribute.add(value);
+
+            if ("cn".equals(key) && (value != null)) {
+              cn = value.toString();
+            }
+          }
+          attributes.put(attribute);
+        }
+      }
     }
 
-    Attribute uac = new BasicAttribute("userAccountControl");  // userAccountControl
-    uac.add("512");
-    attributes.put(uac);
-
-    Attribute passwordAttr = new BasicAttribute("unicodePwd");  // password
-    String quotedPasswordVal = "\"" + password + "\"";
-    try {
-      passwordAttr.add(quotedPasswordVal.getBytes("UTF-16LE"));
-    } catch (UnsupportedEncodingException ue) {
-      throw new KerberosOperationException("Can not encode password with UTF-16LE", ue);
+    if (cn == null) {
+      cn = String.format("%s@%s", principal, realm);
     }
-    attributes.put(passwordAttr);
-
     try {
-      Name name = new CompositeName().add("cn=" + principal + "," + principalContainerDn);
+      Name name = new CompositeName().add(String.format("cn=%s,%s", cn, principalContainerDn));
       ldapContext.createSubcontext(name, attributes);
     } catch (NamingException ne) {
       throw new KerberosOperationException("Can not create principal : " + principal, ne);
@@ -357,7 +443,7 @@ public class ADKerberosOperationHandler extends KerberosOperationHandler {
     properties.put(Context.PROVIDER_URL, ldapUrl);
     properties.put(Context.SECURITY_PRINCIPAL, administratorCredentials.getPrincipal());
     properties.put(Context.SECURITY_CREDENTIALS, administratorCredentials.getPassword());
-    properties.put(Context.SECURITY_AUTHENTICATION, LDAP_ATUH_MECH_SIMPLE);
+    properties.put(Context.SECURITY_AUTHENTICATION, "simple");
     properties.put(Context.REFERRAL, "follow");
     properties.put("java.naming.ldap.factory.socket", TrustingSSLSocketFactory.class.getName());
 
@@ -413,9 +499,71 @@ public class ADKerberosOperationHandler extends KerberosOperationHandler {
    */
   protected SearchControls createSearchControls() {
     SearchControls searchControls = new SearchControls();
-    searchControls.setSearchScope(ONE_LEVEL_SCOPE);
+    searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
     searchControls.setReturningAttributes(new String[]{"cn"});
     return searchControls;
   }
 
+  /**
+   * Processes a Velocity template to generate a map of attributes and values to use to create
+   * Active Directory accounts.
+   * <p/>
+   * If a template was not set, a default template will be used.
+   *
+   * @param context a map of properties to pass to the Velocity engine
+   * @return a Map of attribute names and values to use for creating an Active Directory account
+   * @throws KerberosOperationException if an error occurs processing the template.
+   */
+  protected Map<String, Object> processCreateTemplate(Map<String, Object> context)
+      throws KerberosOperationException {
+
+    if (velocityEngine == null) {
+      throw new KerberosOperationException("The Velocity Engine must not be null");
+    }
+    if (gson == null) {
+      throw new KerberosOperationException("The JSON parser must not be null");
+    }
+
+    Map<String, Object> data = null;
+    String template;
+    StringWriter stringWriter = new StringWriter();
+
+    if ((createTemplate == null) || createTemplate.isEmpty()) {
+      template = "{" +
+          "\"objectClass\": [\"top\", \"person\", \"organizationalPerson\", \"user\"]," +
+          "\"cn\": \"$principal\"," +
+          "#if( $is_service )" +
+          "  \"servicePrincipalName\": \"$principal\"," +
+          "#end" +
+          "\"userPrincipalName\": \"$principal@$realm.toLowerCase()\"," +
+          "\"unicodePwd\": \"\\\"$password\\\"\"," +
+          "\"accountExpires\": \"0\"," +
+          "\"userAccountControl\": \"512\"" +
+          "}";
+    } else {
+      template = createTemplate;
+    }
+
+    try {
+      if (velocityEngine.evaluate(new VelocityContext(context), stringWriter, "Active Directory principal create template", template)) {
+        String json = stringWriter.toString();
+        Type type = new TypeToken<Map<String, Object>>() {
+        }.getType();
+
+        data = gson.fromJson(json, type);
+      }
+    } catch (ParseErrorException e) {
+      LOG.warn("Failed to parse Active Directory create principal template", e);
+      throw new KerberosOperationException("Failed to parse Active Directory create principal template", e);
+    } catch (MethodInvocationException e) {
+      LOG.warn("Failed to process Active Directory create principal template", e);
+      throw new KerberosOperationException("Failed to process Active Directory create principal template", e);
+    } catch (ResourceNotFoundException e) {
+      LOG.warn("Failed to process Active Directory create principal template", e);
+      throw new KerberosOperationException("Failed to process Active Directory create principal template", e);
+    }
+
+    return data;
+  }
+
 }

+ 4 - 20
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosOperationHandler.java

@@ -119,18 +119,6 @@ public abstract class KerberosOperationHandler {
     return passwordBuilder.toString();
   }
 
-  /**
-   * Prepares and creates resources to be used by this KerberosOperationHandler
-   * <p/>
-   * It is expected that this KerberosOperationHandler will not be used before this call.
-   *
-   * @param administratorCredentials a KerberosCredential containing the administrative credentials
-   *                                 for the relevant KDC
-   * @param defaultRealm             a String declaring the default Kerberos realm (or domain)
-   */
-  public abstract void open(KerberosCredential administratorCredentials, String defaultRealm)
-      throws KerberosOperationException;
-
   /**
    * Prepares and creates resources to be used by this KerberosOperationHandler.
    * Implementation in this class is ignoring parameters ldapUrl and principalContainerDn and delegate to
@@ -142,14 +130,10 @@ public abstract class KerberosOperationHandler {
    * @param administratorCredentials a KerberosCredential containing the administrative credentials
    *                                 for the relevant KDC
    * @param defaultRealm             a String declaring the default Kerberos realm (or domain)
-   * @param ldapUrl                  ldapUrl of ldap back end where principals would be created
-   * @param principalContainerDn     DN of the container in ldap back end where principals would be created
+   * @param kerberosConfiguration    a Map of key/value pairs containing data from the kerberos-env configuration set
    */
-  public void open(KerberosCredential administratorCredentials, String defaultRealm,
-                   String ldapUrl, String principalContainerDn)
-      throws KerberosOperationException {
-    open(administratorCredentials, defaultRealm);
-  }
+  public abstract void open(KerberosCredential administratorCredentials, String defaultRealm, Map<String, String> kerberosConfiguration)
+      throws KerberosOperationException;
 
   /**
    * Closes and cleans up any resources used by this KerberosOperationHandler
@@ -178,7 +162,7 @@ public abstract class KerberosOperationHandler {
    *
    * @param principal a String containing the principal to add
    * @param password  a String containing the password to use when creating the principal
-   * @param service a boolean value indicating whether the principal is to be created as a service principal or not
+   * @param service   a boolean value indicating whether the principal is to be created as a service principal or not
    * @return an Integer declaring the generated key number
    * @throws KerberosOperationException
    */

+ 1 - 26
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java

@@ -69,18 +69,6 @@ public abstract class KerberosServerAction extends AbstractServerAction {
    */
   public static final String KDC_TYPE = "kdc_type";
 
-  /**
-   * A (command parameter) property name used to hold the URL for the LDAP interface to the KDC.
-   * This value may be null.
-   */
-  public static final String KDC_LDAP_URL = "kdc_ldap_url";
-
-  /**
-   * A (command parameter) property name used to hold the distinguished name (DN) of the container
-   * in which to store principals within the KDC.  This value may be null.
-   */
-  public static final String KDC_PRINCIPAL_CONTAINER_DN = "kdc_principal_container_dn";
-
   /**
    * The prefix to use for the data directory name.
    */
@@ -293,17 +281,6 @@ public abstract class KerberosServerAction extends AbstractServerAction {
     return KerberosCredential.decrypt(getCommandParameterValue(commandParameters, ADMINISTRATOR_CREDENTIAL), key);
   }
 
-  /**
-   * Attempts to safely retrieve a property with the specified name from the this action's relevant
-   * command parameters Map.
-   *
-   * @param propertyName a String declaring the name of the item from commandParameters to retrieve
-   * @return the value of the requested property, or null if not found or set
-   */
-  protected String getCommandParameterValue(String propertyName) {
-    return getCommandParameterValue(getCommandParameters(), propertyName);
-  }
-
   /**
    * Attempts to safely retrieve the "data_directory" property from the this action's relevant
    * command parameters Map.
@@ -372,10 +349,8 @@ public abstract class KerberosServerAction extends AbstractServerAction {
               throw new AmbariException(message);
             }
 
-            String ldapUrl = getCommandParameterValue(KDC_LDAP_URL);
-            String principalContainerDn = getCommandParameterValue(KDC_PRINCIPAL_CONTAINER_DN);
             try {
-              handler.open(administratorCredential, defaultRealm, ldapUrl, principalContainerDn);
+              handler.open(administratorCredential, defaultRealm, getConfiguration("kerberos-env"));
             } catch (KerberosOperationException e) {
               String message = String.format("Failed to process the identities, could not properly open the KDC operation handler: %s",
                   e.getMessage());

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

@@ -28,6 +28,7 @@ import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -49,10 +50,28 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
   private final static Logger LOG = LoggerFactory.getLogger(MITKerberosOperationHandler.class);
 
 
+  /**
+   * Prepares and creates resources to be used by this KerberosOperationHandler
+   * <p/>
+   * It is expected that this KerberosOperationHandler will not be used before this call.
+   * <p/>
+   * The kerberosConfiguration Map is not being used.
+   *
+   * @param administratorCredentials a KerberosCredential containing the administrative credentials
+   *                                 for the relevant KDC
+   * @param realm                    a String declaring the default Kerberos realm (or domain)
+   * @param kerberosConfiguration    a Map of key/value pairs containing data from the kerberos-env configuration set
+   * @throws KerberosKDCConnectionException       if a connection to the KDC cannot be made
+   * @throws KerberosAdminAuthenticationException if the administrator credentials fail to authenticate
+   * @throws KerberosRealmException               if the realm does not map to a KDC
+   * @throws KerberosOperationException           if an unexpected error occurred
+   */
   @Override
-  public void open(KerberosCredential administratorCredentials, String defaultRealm) throws KerberosOperationException {
+  public void open(KerberosCredential administratorCredentials, String realm,
+                   Map<String, String> kerberosConfiguration)
+      throws KerberosOperationException {
     setAdministratorCredentials(administratorCredentials);
-    setDefaultRealm(defaultRealm);
+    setDefaultRealm(realm);
     setOpen(true);
   }
 

+ 3 - 3
ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java

@@ -88,9 +88,9 @@ public class ConfigHelper {
   public Map<String, Map<String, String>> getEffectiveDesiredTags(
       Cluster cluster, String hostName) throws AmbariException {
 
-    Host host = clusters.getHost(hostName);
-
-    return getEffectiveDesiredTags(cluster, host.getDesiredHostConfigs(cluster));
+    Host host = (hostName == null) ? null : clusters.getHost(hostName);
+    Map<String, HostConfig> desiredHostConfigs = (host == null) ? null : host.getDesiredHostConfigs(cluster);
+    return getEffectiveDesiredTags(cluster, desiredHostConfigs);
   }
 
   /**

+ 21 - 0
ambari-server/src/main/resources/common-services/KERBEROS/1.10.3-10/configuration/kerberos-env.xml

@@ -36,4 +36,25 @@
     </description>
     <value></value>
   </property>
+
+  <property require-input="true">
+    <name>create_attributes_template</name>
+    <description>
+      A Velocity template to use to generate a JSON-formatted document containing the set of
+      attribute names and values needed to create a new Kerberos identity in the relevant KDC.
+    </description>
+    <value>
+{
+  "objectClass": ["top", "person", "organizationalPerson", "user"],
+  "cn": "$principal",
+  #if( $is_service )
+  "servicePrincipalName": "$principal",
+  #end
+  "userPrincipalName": "$principal@$realm.toLowerCase()",
+  "unicodePwd": "\"$password\"",
+  "accountExpires": "0",
+  "userAccountControl": "512"
+}
+    </value>
+  </property>
 </configuration>

+ 1 - 1
ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionDBAccessorImpl.java

@@ -581,7 +581,7 @@ public class TestActionDBAccessorImpl {
     Stage s = new Stage(requestId, "/a/b", "cluster1", 1L, "action db accessor test",
         "", "commandParamsStage", "hostParamsStage");
     s.setStageId(stageId);
-    s.addServerActionCommand(serverActionName, Role.AMBARI_SERVER_ACTION, RoleCommand.ACTIONEXECUTE, clusterName, null, null, "command details", 300, false);
+    s.addServerActionCommand(serverActionName, Role.AMBARI_SERVER_ACTION, RoleCommand.ACTIONEXECUTE, clusterName, null, null, "command details", null, 300, false);
     List<Stage> stages = new ArrayList<Stage>();
     stages.add(s);
     final RequestResourceFilter resourceFilter = new RequestResourceFilter("AMBARI", "SERVER", Arrays.asList(hostname));

+ 1 - 1
ambari-server/src/test/java/org/apache/ambari/server/actionmanager/TestActionScheduler.java

@@ -769,7 +769,7 @@ public class TestActionScheduler {
         RoleCommand.EXECUTE, "cluster1",
         new ServiceComponentHostServerActionEvent(serverHostname, System.currentTimeMillis()),
         payload,
-        null, timeout, false);
+        null, null, timeout, false);
 
     return stage;
   }

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

@@ -88,7 +88,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(kerberosOperationHandlerFactory.getKerberosOperationHandler(KDCType.MIT_KDC))
         .andReturn(new KerberosOperationHandler() {
           @Override
-          public void open(KerberosCredential administratorCredentials, String defaultRealm) throws KerberosOperationException {
+          public void open(KerberosCredential administratorCredentials, String defaultRealm, Map<String, String> kerberosConfiguration) throws KerberosOperationException {
             setAdministratorCredentials(administratorCredentials);
             setDefaultRealm(defaultRealm);
             setOpen(true);

+ 2 - 2
ambari-server/src/test/java/org/apache/ambari/server/serveraction/ServerActionExecutorTest.java

@@ -99,7 +99,7 @@ public class ServerActionExecutorTest {
         RoleCommand.EXECUTE,
         "cluster1", SERVER_HOST_NAME,
         new ServiceComponentHostServerActionEvent(StageUtils.getHostName(), System.currentTimeMillis()),
-        Collections.<String, String>emptyMap(), null, 1200, false);
+        Collections.<String, String>emptyMap(), null, null, 1200, false);
 
     final List<Stage> stages = new ArrayList<Stage>() {
       {
@@ -289,7 +289,7 @@ public class ServerActionExecutorTest {
     stage.addServerActionCommand(MockServerAction.class.getName(), Role.AMBARI_SERVER_ACTION,
         RoleCommand.EXECUTE, "cluster1",
         new ServiceComponentHostServerActionEvent(SERVER_HOST_NAME, System.currentTimeMillis()),
-        payload, "command detail", timeout, false);
+        payload, "command detail", null, timeout, false);
 
     return stage;
   }

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

@@ -18,6 +18,7 @@
 
 package org.apache.ambari.server.serveraction.kerberos;
 
+import junit.framework.Assert;
 import org.easymock.EasyMockSupport;
 import org.easymock.IAnswer;
 import org.junit.Ignore;
@@ -31,6 +32,10 @@ import javax.naming.directory.SearchResult;
 import javax.naming.ldap.Control;
 import javax.naming.ldap.LdapContext;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
 
 import static org.easymock.EasyMock.anyObject;
@@ -48,7 +53,12 @@ public class ADKerberosOperationHandlerTest extends EasyMockSupport {
   public void testOpenExceptionLdapUrlNotProvided() throws Exception {
     KerberosOperationHandler handler = new ADKerberosOperationHandler();
     KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
-    handler.open(kc, DEFAULT_REALM, null, DEFAULT_PRINCIPAL_CONTAINER_DN);
+    Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
+      {
+        put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN);
+      }
+    };
+    handler.open(kc, DEFAULT_REALM, kerberosEnvMap);
     handler.close();
   }
 
@@ -56,19 +66,38 @@ public class ADKerberosOperationHandlerTest extends EasyMockSupport {
   public void testOpenExceptionPrincipalContainerDnNotProvided() throws Exception {
     KerberosOperationHandler handler = new ADKerberosOperationHandler();
     KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
-    handler.open(kc, DEFAULT_REALM, DEFAULT_LDAP_URL, null);
+    Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
+      {
+        put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
+      }
+    };
+    handler.open(kc, DEFAULT_REALM, kerberosEnvMap);
     handler.close();
   }
 
   @Test(expected = KerberosAdminAuthenticationException.class)
   public void testOpenExceptionAdminCredentialsNotProvided() throws Exception {
     KerberosOperationHandler handler = new ADKerberosOperationHandler();
-    handler.open(null, DEFAULT_REALM, DEFAULT_LDAP_URL, DEFAULT_PRINCIPAL_CONTAINER_DN);
+    Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
+      {
+        put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
+        put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN);
+      }
+    };
+    handler.open(null, DEFAULT_REALM, kerberosEnvMap);
     handler.close();
   }
 
   @Test(expected = KerberosAdminAuthenticationException.class)
   public void testTestAdministratorCredentialsIncorrectAdminPassword() throws Exception {
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, "wrong", null);
+    Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
+      {
+        put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
+        put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN);
+      }
+    };
+
     ADKerberosOperationHandler handler = createMockBuilder(ADKerberosOperationHandler.class)
         .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class))
         .createNiceMock();
@@ -82,14 +111,21 @@ public class ADKerberosOperationHandlerTest extends EasyMockSupport {
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, "wrong", null),
-        DEFAULT_REALM, DEFAULT_LDAP_URL, DEFAULT_PRINCIPAL_CONTAINER_DN);
+    handler.open(kc, DEFAULT_REALM, kerberosEnvMap);
     handler.testAdministratorCredentials();
     handler.close();
   }
 
   @Test(expected = KerberosAdminAuthenticationException.class)
   public void testTestAdministratorCredentialsIncorrectAdminPrincipal() throws Exception {
+    KerberosCredential kc = new KerberosCredential("wrong", DEFAULT_ADMIN_PASSWORD, null);
+    Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
+      {
+        put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
+        put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN);
+      }
+    };
+
     ADKerberosOperationHandler handler = createMockBuilder(ADKerberosOperationHandler.class)
         .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class))
         .createNiceMock();
@@ -103,14 +139,21 @@ public class ADKerberosOperationHandlerTest extends EasyMockSupport {
 
     replayAll();
 
-    handler.open(new KerberosCredential("wrong", DEFAULT_ADMIN_PASSWORD, null),
-        DEFAULT_REALM, DEFAULT_LDAP_URL, DEFAULT_PRINCIPAL_CONTAINER_DN);
+    handler.open(kc, DEFAULT_REALM, kerberosEnvMap);
     handler.testAdministratorCredentials();
     handler.close();
   }
 
   @Test(expected = KerberosKDCConnectionException.class)
   public void testTestAdministratorCredentialsKDCConnectionException() throws Exception {
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
+    Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
+      {
+        put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, "invalid");
+        put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN);
+      }
+    };
+
     ADKerberosOperationHandler handler = createMockBuilder(ADKerberosOperationHandler.class)
         .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class))
         .createNiceMock();
@@ -124,8 +167,7 @@ public class ADKerberosOperationHandlerTest extends EasyMockSupport {
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null),
-        DEFAULT_REALM, "invalid", DEFAULT_PRINCIPAL_CONTAINER_DN);
+    handler.open(kc, DEFAULT_REALM, kerberosEnvMap);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -133,6 +175,14 @@ public class ADKerberosOperationHandlerTest extends EasyMockSupport {
 
   @Test
   public void testTestAdministratorCredentialsSuccess() throws Exception {
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
+    Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
+      {
+        put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
+        put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN);
+      }
+    };
+
     ADKerberosOperationHandler handler = createMockBuilder(ADKerberosOperationHandler.class)
         .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class))
         .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createSearchControls"))
@@ -170,12 +220,197 @@ public class ADKerberosOperationHandlerTest extends EasyMockSupport {
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null),
-        DEFAULT_REALM, DEFAULT_LDAP_URL, DEFAULT_PRINCIPAL_CONTAINER_DN);
+    handler.open(kc, DEFAULT_REALM, kerberosEnvMap);
     handler.testAdministratorCredentials();
     handler.close();
   }
 
+  @Test
+  public void testProcessCreateTemplateDefault() throws Exception {
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
+    Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
+      {
+        put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
+        put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN);
+      }
+    };
+
+    ADKerberosOperationHandler handler = createMockBuilder(ADKerberosOperationHandler.class)
+        .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class))
+        .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createSearchControls"))
+        .createNiceMock();
+
+    expect(handler.createInitialLdapContext(anyObject(Properties.class), anyObject(Control[].class)))
+        .andAnswer(new IAnswer<LdapContext>() {
+          @Override
+          public LdapContext answer() throws Throwable {
+            LdapContext ldapContext = createNiceMock(LdapContext.class);
+            expect(ldapContext.search(anyObject(String.class), anyObject(String.class), anyObject(SearchControls.class)))
+                .andAnswer(new IAnswer<NamingEnumeration<SearchResult>>() {
+                  @Override
+                  public NamingEnumeration<SearchResult> answer() throws Throwable {
+                    NamingEnumeration<SearchResult> result = createNiceMock(NamingEnumeration.class);
+                    expect(result.hasMore()).andReturn(false).once();
+                    replay(result);
+                    return result;
+                  }
+                })
+                .once();
+            replay(ldapContext);
+            return ldapContext;
+          }
+        })
+        .once();
+    expect(handler.createSearchControls()).andAnswer(new IAnswer<SearchControls>() {
+      @Override
+      public SearchControls answer() throws Throwable {
+        SearchControls searchControls = createNiceMock(SearchControls.class);
+        replay(searchControls);
+        return searchControls;
+      }
+    }).once();
+
+    replayAll();
+
+    handler.open(kc, DEFAULT_REALM, kerberosEnvMap);
+
+    Map<String, Object> context = new HashMap<String, Object>();
+    context.put("principal", "nn/c6501.ambari.apache.org");
+    context.put("principal_primary", "nn");
+    context.put("principal_instance", "c6501.ambari.apache.org");
+    context.put("realm", "EXAMPLE.COM");
+    context.put("realm_lowercase", "example.com");
+    context.put("password", "secret");
+    context.put("is_service", true);
+    context.put("container_dn", "ou=cluster,DC=EXAMPLE,DC=COM");
+
+    Map<String, Object> data;
+
+    data = handler.processCreateTemplate(context);
+
+    Assert.assertNotNull(data);
+    Assert.assertEquals(7, data.size());
+    Assert.assertEquals(new ArrayList<String>(Arrays.asList("top", "person", "organizationalPerson", "user")), data.get("objectClass"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org", data.get("cn"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org", data.get("servicePrincipalName"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org@example.com", data.get("userPrincipalName"));
+    Assert.assertEquals("\"secret\"", data.get("unicodePwd"));
+    Assert.assertEquals("0", data.get("accountExpires"));
+    Assert.assertEquals("512", data.get("userAccountControl"));
+
+
+    context.put("is_service", false);
+    data = handler.processCreateTemplate(context);
+
+    Assert.assertNotNull(data);
+    Assert.assertEquals(6, data.size());
+    Assert.assertEquals(new ArrayList<String>(Arrays.asList("top", "person", "organizationalPerson", "user")), data.get("objectClass"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org", data.get("cn"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org@example.com", data.get("userPrincipalName"));
+    Assert.assertEquals("\"secret\"", data.get("unicodePwd"));
+    Assert.assertEquals("0", data.get("accountExpires"));
+    Assert.assertEquals("512", data.get("userAccountControl"));
+
+    handler.close();
+  }
+
+  @Test
+  public void testProcessCreateTemplateCustom() throws Exception {
+    KerberosCredential kc = new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null);
+    Map<String, String> kerberosEnvMap = new HashMap<String, String>() {
+      {
+        put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL);
+        put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN);
+        put(ADKerberosOperationHandler.KERBEROS_ENV_CREATE_ATTRIBUTES_TEMPLATE, "{" +
+            "  \"objectClass\": [" +
+            "    \"top\"," +
+            "    \"person\"," +
+            "    \"organizationalPerson\"," +
+            "    \"user\"" +
+            "  ]," +
+            "  \"cn\": \"$principal@$realm\"," +
+            "  \"dn\": \"$principal@$realm,$container_dn\"," +
+            "  \"distinguishedName\": \"$principal@$realm,$container_dn\"," +
+            "  \"sAMAccountName\": \"$principal\"," +
+            "  #if( $is_service )" +
+            "  \"servicePrincipalName\": \"$principal\"," +
+            "  #end" +
+            "  \"userPrincipalName\": \"$principal@$realm.toLowerCase()\"," +
+            "  \"unicodePwd\": \"`$password`\"," +
+            "  \"accountExpires\": \"0\"," +
+            "  \"userAccountControl\": \"66048\"" +
+            "}");
+      }
+    };
+
+    ADKerberosOperationHandler handler = createMockBuilder(ADKerberosOperationHandler.class)
+        .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class))
+        .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createSearchControls"))
+        .createNiceMock();
+
+    expect(handler.createInitialLdapContext(anyObject(Properties.class), anyObject(Control[].class)))
+        .andAnswer(new IAnswer<LdapContext>() {
+          @Override
+          public LdapContext answer() throws Throwable {
+            LdapContext ldapContext = createNiceMock(LdapContext.class);
+            expect(ldapContext.search(anyObject(String.class), anyObject(String.class), anyObject(SearchControls.class)))
+                .andAnswer(new IAnswer<NamingEnumeration<SearchResult>>() {
+                  @Override
+                  public NamingEnumeration<SearchResult> answer() throws Throwable {
+                    NamingEnumeration<SearchResult> result = createNiceMock(NamingEnumeration.class);
+                    expect(result.hasMore()).andReturn(false).once();
+                    replay(result);
+                    return result;
+                  }
+                })
+                .once();
+            replay(ldapContext);
+            return ldapContext;
+          }
+        })
+        .once();
+    expect(handler.createSearchControls()).andAnswer(new IAnswer<SearchControls>() {
+      @Override
+      public SearchControls answer() throws Throwable {
+        SearchControls searchControls = createNiceMock(SearchControls.class);
+        replay(searchControls);
+        return searchControls;
+      }
+    }).once();
+
+    replayAll();
+
+    handler.open(kc, DEFAULT_REALM, kerberosEnvMap);
+
+
+    Map<String, Object> context = new HashMap<String, Object>();
+    context.put("principal", "nn/c6501.ambari.apache.org");
+    context.put("principal_primary", "nn");
+    context.put("principal_instance", "c6501.ambari.apache.org");
+    context.put("realm", "EXAMPLE.COM");
+    context.put("realm_lowercase", "example.com");
+    context.put("password", "secret");
+    context.put("is_service", true);
+    context.put("container_dn", "ou=cluster,DC=EXAMPLE,DC=COM");
+
+    Map<String, Object> data = handler.processCreateTemplate(context);
+
+    Assert.assertNotNull(data);
+    Assert.assertEquals(10, data.size());
+    Assert.assertEquals(new ArrayList<String>(Arrays.asList("top", "person", "organizationalPerson", "user")), data.get("objectClass"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org@EXAMPLE.COM", data.get("cn"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org", data.get("servicePrincipalName"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org@example.com", data.get("userPrincipalName"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org", data.get("sAMAccountName"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org@EXAMPLE.COM,ou=cluster,DC=EXAMPLE,DC=COM", data.get("distinguishedName"));
+    Assert.assertEquals("nn/c6501.ambari.apache.org@EXAMPLE.COM,ou=cluster,DC=EXAMPLE,DC=COM", data.get("dn"));
+    Assert.assertEquals("`secret`", data.get("unicodePwd"));
+    Assert.assertEquals("0", data.get("accountExpires"));
+    Assert.assertEquals("66048", data.get("userAccountControl"));
+
+    handler.close();
+  }
+
   /**
    * Implementation to illustrate the use of operations on this class
    *
@@ -184,18 +419,6 @@ public class ADKerberosOperationHandlerTest extends EasyMockSupport {
   @Test
   @Ignore
   public void testLive() throws Throwable {
-
-    /* ******************************************************************************************
-     * SSL Certificate of AD should have been imported into truststore when that certificate
-     * is not issued by trusted authority. This is typical with self signed certificated in
-     * development environment.  To use specific trust store, set path to it in
-     * javax.net.ssl.trustStore System property.  Example:
-     *      System.setProperty(
-     *        "javax.net.ssl.trustStore",
-     *        "/tmp/workspace/ambari/apache-ambari-rd/cacerts"
-     *       );
-     * ****************************************************************************************** */
-
     ADKerberosOperationHandler handler = new ADKerberosOperationHandler();
     String principal = System.getProperty("principal");
     String password = System.getProperty("password");
@@ -224,16 +447,36 @@ public class ADKerberosOperationHandlerTest extends EasyMockSupport {
     }
 
     KerberosCredential credentials = new KerberosCredential(principal, password, null);
+    Map<String, String> kerberosEnvMap = new HashMap<String, String>();
+
+    kerberosEnvMap.put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, ldapUrl);
+    kerberosEnvMap.put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, containerDN);
 
-    handler.open(credentials, realm, ldapUrl, containerDN);
+    handler.open(credentials, realm, kerberosEnvMap);
 
     System.out.println("Test Admin Credentials: " + handler.testAdministratorCredentials());
     // does the principal already exist?
     System.out.println("Principal exists: " + handler.principalExists("nn/c1508.ambari.apache.org"));
 
     //create principal
-    handler.createPrincipal("nn/c1508.ambari.apache.org@" + DEFAULT_REALM.toLowerCase(), handler.createSecurePassword(), true);
-    handler.createPrincipal("nn/c1508.ambari.apache.org", handler.createSecurePassword(), true);
+//    handler.createPrincipal("nn/c1508.ambari.apache.org@" + DEFAULT_REALM, handler.createSecurePassword(), true);
+
+    handler.close();
+
+    kerberosEnvMap.put(ADKerberosOperationHandler.KERBEROS_ENV_CREATE_ATTRIBUTES_TEMPLATE, "{" +
+        "\"objectClass\": [\"top\", \"person\", \"organizationalPerson\", \"user\"]," +
+        "\"distinguishedName\": \"CN=$principal@$realm,$container_dn\"," +
+        "#if( $is_service )" +
+        "\"servicePrincipalName\": \"$principal\"," +
+        "#end" +
+        "\"userPrincipalName\": \"$principal@$realm.toLowerCase()\"," +
+        "\"unicodePwd\": \"\\\"$password\\\"\"," +
+        "\"accountExpires\": \"0\"," +
+        "\"userAccountControl\": \"66048\"" +
+        "}");
+
+    handler.open(credentials, realm, kerberosEnvMap);
+    handler.createPrincipal("abcdefg/c1509.ambari.apache.org@" + DEFAULT_REALM, handler.createSecurePassword(), true);
 
     //update the password
     handler.setPrincipalPassword("nn/c1508.ambari.apache.org", handler.createSecurePassword());
@@ -243,5 +486,4 @@ public class ADKerberosOperationHandlerTest extends EasyMockSupport {
 
     handler.close();
   }
-
-}
+}

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

@@ -30,6 +30,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 public abstract class KerberosOperationHandlerTest {
@@ -204,7 +205,7 @@ public abstract class KerberosOperationHandlerTest {
     KerberosOperationHandler handler = new KerberosOperationHandler() {
 
       @Override
-      public void open(KerberosCredential administratorCredentials, String defaultRealm) throws KerberosOperationException {
+      public void open(KerberosCredential administratorCredentials, String defaultRealm, Map<String, String> kerberosConfiguration) throws KerberosOperationException {
         setAdministratorCredentials(administratorCredentials);
         setDefaultRealm(defaultRealm);
       }
@@ -220,7 +221,7 @@ public abstract class KerberosOperationHandlerTest {
       }
 
       @Override
-      public Integer createPrincipal(String principal, String password, boolean serivce) throws KerberosOperationException{
+      public Integer createPrincipal(String principal, String password, boolean service) throws KerberosOperationException {
         return 0;
       }
 
@@ -235,7 +236,7 @@ public abstract class KerberosOperationHandlerTest {
       }
     };
 
-    handler.open(new KerberosCredential("admin/admin", "hadoop", null), "EXAMPLE.COM");
+    handler.open(new KerberosCredential("admin/admin", "hadoop", null), "EXAMPLE.COM", null);
     return handler;
   }
 }

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

@@ -135,8 +135,8 @@ public class KerberosServerActionTest {
 
   @Test
   public void testGetCommandParameterValueStatic() throws Exception {
-    Assert.assertNull(action.getCommandParameterValue("nonexistingvalue"));
-    Assert.assertEquals("REALM.COM", action.getCommandParameterValue(KerberosServerAction.DEFAULT_REALM));
+    Assert.assertNull(KerberosServerAction.getCommandParameterValue(commandParams, "nonexistingvalue"));
+    Assert.assertEquals("REALM.COM", KerberosServerAction.getCommandParameterValue(commandParams, KerberosServerAction.DEFAULT_REALM));
   }
 
   @Test
@@ -170,13 +170,6 @@ public class KerberosServerActionTest {
     Assert.assertNotNull(KerberosServerAction.getPrincipalPasswordMap(sharedMap));
   }
 
-  @Test
-  public void testGetCommandParameterValue() throws Exception {
-    Assert.assertNull(action.getCommandParameterValue("invalid_parameter"));
-    Assert.assertEquals(commandParams.get(KerberosServerAction.DEFAULT_REALM),
-        action.getCommandParameterValue(KerberosServerAction.DEFAULT_REALM));
-  }
-
   @Test
   public void testGetDataDirectoryPath() throws Exception {
     Assert.assertEquals(temporaryDirectory.getAbsolutePath(), action.getDataDirectoryPath());

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

@@ -39,7 +39,7 @@ public class MITKerberosOperationHandlerTest extends EasyMockSupport {
   @Test
   public void testSetPrincipalPasswordExceptions() throws Exception {
     MITKerberosOperationHandler handler = new MITKerberosOperationHandler();
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, null);
 
     try {
       handler.setPrincipalPassword(DEFAULT_ADMIN_PRINCIPAL, null);
@@ -75,7 +75,7 @@ public class MITKerberosOperationHandlerTest extends EasyMockSupport {
   @Test
   public void testCreateServicePrincipalExceptions() throws Exception {
     MITKerberosOperationHandler handler = new MITKerberosOperationHandler();
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, null);
 
     try {
       handler.createPrincipal(DEFAULT_ADMIN_PRINCIPAL, null, false);
@@ -134,7 +134,7 @@ public class MITKerberosOperationHandlerTest extends EasyMockSupport {
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, null);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -167,7 +167,7 @@ public class MITKerberosOperationHandlerTest extends EasyMockSupport {
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, null);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -200,7 +200,7 @@ public class MITKerberosOperationHandlerTest extends EasyMockSupport {
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, null);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -233,7 +233,7 @@ public class MITKerberosOperationHandlerTest extends EasyMockSupport {
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, null);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -266,7 +266,7 @@ public class MITKerberosOperationHandlerTest extends EasyMockSupport {
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, null);
     Assert.assertFalse(handler.testAdministratorCredentials());
     handler.close();
   }
@@ -319,7 +319,7 @@ public class MITKerberosOperationHandlerTest extends EasyMockSupport {
 
     replayAll();
 
-    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM);
+    handler.open(new KerberosCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, null), DEFAULT_REALM, null);
     handler.testAdministratorCredentials();
     handler.close();
   }
@@ -346,7 +346,7 @@ public class MITKerberosOperationHandlerTest extends EasyMockSupport {
 
     KerberosCredential credentials = new KerberosCredential(principal, password, null);
 
-    handler.open(credentials, realm);
+    handler.open(credentials, realm, null);
     handler.testAdministratorCredentials();
     handler.close();
   }