From a904f75f0900fe97d7a6009abc7f97164fbdb020 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 20 Sep 2011 08:41:42 +0000
Subject: [PATCH] Issue OPENDJ-262: Implement pass through authentication (PTA)

---
 opendj-sdk/opends/src/messages/messages/extension.properties                                                                       |   19 ++
 opendj-sdk/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java                          |  143 +++++++++++++++++++-
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/LDAPPassThroughAuthenticationPolicyConfiguration.xml                  |   90 +++++++++++-
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java |   93 +++++++++++++
 opendj-sdk/opends/resource/schema/02-config.ldif                                                                                   |   18 ++
 opendj-sdk/opends/src/admin/messages/LDAPPassThroughAuthenticationPolicyCfgDefn.properties                                         |    7 
 6 files changed, 349 insertions(+), 21 deletions(-)

diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index 3f04238..267278a 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -2616,6 +2616,21 @@
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
   SINGLE-VALUE
   X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.20
+  NAME 'ds-cfg-mapped-search-bind-password-property'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.21
+  NAME 'ds-cfg-mapped-search-bind-password-environment-variable'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.22
+  NAME 'ds-cfg-mapped-search-bind-password-file'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDJ Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler'
   SUP top
@@ -4367,6 +4382,9 @@
         ds-cfg-mapped-attribute $
         ds-cfg-mapped-search-bind-dn $
         ds-cfg-mapped-search-bind-password $
+        ds-cfg-mapped-search-bind-password-property $
+        ds-cfg-mapped-search-bind-password-environment-variable $
+        ds-cfg-mapped-search-bind-password-file $
         ds-cfg-mapped-search-base-dn $
         ds-cfg-connection-timeout $
         ds-cfg-trust-manager-provider $
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/LDAPPassThroughAuthenticationPolicyConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/LDAPPassThroughAuthenticationPolicyConfiguration.xml
index d9e560c..53fa9ee 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/LDAPPassThroughAuthenticationPolicyConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/LDAPPassThroughAuthenticationPolicyConfiguration.xml
@@ -60,18 +60,37 @@
   <adm:constraint>
     <adm:synopsis>
       One or more search base DNs must be specified when using the
-      "mapped-search" mapping policies.
+      "mapped-search" mapping policy.
     </adm:synopsis>
     <adm:condition>
       <adm:implies>
-        <adm:or>
-          <adm:contains property="mapping-policy" value="mapped-search" />
-        </adm:or>
+        <adm:contains property="mapping-policy" value="mapped-search" />
         <adm:is-present property="mapped-search-base-dn" />
       </adm:implies>
     </adm:condition>
   </adm:constraint>
 
+  <adm:constraint>
+    <adm:synopsis>
+      The mapped search bind password must be specified when using the
+      "mapped-search" mapping policy and a mapped-search-bind-dn is defined.
+    </adm:synopsis>
+    <adm:condition>
+      <adm:implies>
+        <adm:and>
+          <adm:contains property="mapping-policy" value="mapped-search" />
+          <adm:is-present property="mapped-search-bind-dn" />
+        </adm:and>
+        <adm:or>
+          <adm:is-present property="mapped-search-bind-password" />
+          <adm:is-present property="mapped-search-bind-password-property" />
+          <adm:is-present property="mapped-search-bind-password-environment-variable" />
+          <adm:is-present property="mapped-search-bind-password-file" />
+        </adm:or>
+      </adm:implies>
+    </adm:condition>
+  </adm:constraint>
+
   <adm:profile name="ldap">
     <ldap:object-class>
       <ldap:name>ds-cfg-ldap-pass-through-authentication-policy</ldap:name>
@@ -330,9 +349,7 @@
       user searches in the remote LDAP directory service.
     </adm:synopsis>
     <adm:default-behavior>
-      <adm:alias>
-        <adm:synopsis>Searches will be performed anonymously.</adm:synopsis>
-      </adm:alias>
+      <adm:undefined/>
     </adm:default-behavior>
     <adm:syntax>
       <adm:password />
@@ -344,6 +361,65 @@
     </adm:profile>
   </adm:property>
 
+  <adm:property name="mapped-search-bind-password-property">
+    <adm:synopsis>
+      Specifies the name of a Java property containing the bind password which
+      should be used to perform user searches in the remote LDAP directory
+      service.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:undefined/>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-mapped-search-bind-password-property</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+  <adm:property name="mapped-search-bind-password-environment-variable">
+    <adm:synopsis>
+      Specifies the name of an environment variable containing the bind 
+      password which should be used to perform user searches in the remote LDAP
+      directory service.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:undefined/>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>
+          ds-cfg-mapped-search-bind-password-environment-variable
+        </ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+  <adm:property name="mapped-search-bind-password-file">
+    <adm:synopsis>
+      Specifies the name of a file containing the bind 
+      password which should be used to perform user searches in the remote LDAP
+      directory service.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:undefined/>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-mapped-search-bind-password-file</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
   <adm:property name="mapped-search-base-dn" multi-valued="true">
     <adm:synopsis>
       Specifies the set of base DNs below which to search for users
diff --git a/opendj-sdk/opends/src/admin/messages/LDAPPassThroughAuthenticationPolicyCfgDefn.properties b/opendj-sdk/opends/src/admin/messages/LDAPPassThroughAuthenticationPolicyCfgDefn.properties
index d133572..33c8e3d 100644
--- a/opendj-sdk/opends/src/admin/messages/LDAPPassThroughAuthenticationPolicyCfgDefn.properties
+++ b/opendj-sdk/opends/src/admin/messages/LDAPPassThroughAuthenticationPolicyCfgDefn.properties
@@ -3,7 +3,8 @@
 synopsis=An authentication policy for users whose credentials are managed by a remote LDAP directory service.
 description=Authentication attempts will be redirected to the remote LDAP directory service based on a combination of the criteria specified in this policy and the content of the user's entry in this directory server.
 constraint.1.synopsis=One or more mapped attributes must be specified when using the "mapped-bind" or "mapped-search" mapping policies.
-constraint.2.synopsis=One or more search base DNs must be specified when using the "mapped-search" mapping policies.
+constraint.2.synopsis=One or more search base DNs must be specified when using the "mapped-search" mapping policy.
+constraint.3.synopsis=The mapped search bind password must be specified when using the "mapped-search" mapping policy and a mapped-search-bind-dn is defined.
 property.connection-timeout.synopsis=Specifies the timeout used when connecting to remote LDAP director servers, performing SSL negotiation, and for individual search and bind requests.
 property.connection-timeout.description=If the timeout expires then the current operation will be aborted and retried against another LDAP server if one is available.
 property.java-class.synopsis=Specifies the fully-qualified name of the Java class which provides the LDAP Pass Through Authentication Policy implementation.
@@ -14,7 +15,9 @@
 property.mapped-search-bind-dn.synopsis=Specifies the bind DN which should be used to perform user searches in the remote LDAP directory service.
 property.mapped-search-bind-dn.default-behavior.alias.synopsis=Searches will be performed anonymously.
 property.mapped-search-bind-password.synopsis=Specifies the bind password which should be used to perform user searches in the remote LDAP directory service.
-property.mapped-search-bind-password.default-behavior.alias.synopsis=Searches will be performed anonymously.
+property.mapped-search-bind-password-environment-variable.synopsis=Specifies the name of an environment variable containing the bind password which should be used to perform user searches in the remote LDAP directory service.
+property.mapped-search-bind-password-file.synopsis=Specifies the name of a file containing the bind password which should be used to perform user searches in the remote LDAP directory service.
+property.mapped-search-bind-password-property.synopsis=Specifies the name of a Java property containing the bind password which should be used to perform user searches in the remote LDAP directory service.
 property.mapping-policy.synopsis=Specifies the mapping algorithm for obtaining the bind DN from the user's entry.
 property.mapping-policy.syntax.enumeration.value.mapped-bind.synopsis=Bind to the remote LDAP directory service using a DN obtained from an attribute in the user's entry. This policy will check each attribute named in the "mapped-attribute" property. If more than one attribute or value is present then the first one will be used.
 property.mapping-policy.syntax.enumeration.value.mapped-search.synopsis=Bind to the remote LDAP directory service using the DN of an entry obtained using a search against the remote LDAP directory service. The search filter will comprise of an equality matching filter whose attribute type is the "mapped-attribute" property, and whose assertion value is the attribute value obtained from the user's entry. If more than one attribute or value is present then the filter will be composed of multiple equality filters combined using a logical OR (union).
diff --git a/opendj-sdk/opends/src/messages/messages/extension.properties b/opendj-sdk/opends/src/messages/messages/extension.properties
index edd2010..2412677 100644
--- a/opendj-sdk/opends/src/messages/messages/extension.properties
+++ b/opendj-sdk/opends/src/messages/messages/extension.properties
@@ -1498,3 +1498,22 @@
 MILD_ERR_LDAP_PTA_INVALID_PORT_NUMBER_606=The configuration of LDAP PTA policy \
  "%s" is invalid because the remote LDAP server address "%s" specifies a port \
  number which is invalid. Port numbers should be greater than 0 and less than 65536 
+SEVERE_ERR_LDAP_PTA_PWD_PROPERTY_NOT_SET_607=The configuration of LDAP PTA policy \
+ "%s" is invalid because the Java property %s which should contain the mapped \
+ search bind password is not set
+SEVERE_ERR_LDAP_PTA_PWD_ENVAR_NOT_SET_608=The configuration of LDAP PTA policy \
+ "%s" is invalid because the environment variable %s which should contain the mapped \
+ search bind password is not set
+SEVERE_ERR_LDAP_PTA_PWD_NO_SUCH_FILE_609=The configuration of LDAP PTA policy \
+ "%s" is invalid because the file %s which should contain the mapped search \
+ bind password does not exist
+SEVERE_ERR_LDAP_PTA_PWD_FILE_CANNOT_READ_610=The configuration of LDAP PTA policy \
+ "%s" is invalid because the file %s which should contain the mapped search \
+ bind password cannot be read for the following reason:  %s
+SEVERE_ERR_LDAP_PTA_PWD_FILE_EMPTY_611=The configuration of LDAP PTA policy \
+ "%s" is invalid because the file %s which should contain the mapped search \
+ bind password is empty
+SEVERE_ERR_LDAP_PTA_NO_PWD_613=The configuration of LDAP PTA policy \
+ "%s" is invalid because it does not specify the a means for obtaining the mapped \
+ search bind password
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
index 02702cd..304f2a8 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyFactory.java
@@ -32,9 +32,10 @@
 import static org.opends.messages.ExtensionMessages.*;
 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
 import static org.opends.server.protocols.ldap.LDAPConstants.*;
+import static org.opends.server.util.StaticUtils.getExceptionMessage;
+import static org.opends.server.util.StaticUtils.getFileForPath;
 
-import java.io.Closeable;
-import java.io.IOException;
+import java.io.*;
 import java.net.*;
 import java.util.*;
 import java.util.concurrent.*;
@@ -47,6 +48,8 @@
 
 import org.opends.messages.Message;
 import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.meta.
+  LDAPPassThroughAuthenticationPolicyCfgDefn.MappingPolicy;
 import org.opends.server.admin.std.server.*;
 import org.opends.server.api.*;
 import org.opends.server.config.ConfigException;
@@ -69,7 +72,6 @@
 {
 
   // TODO: handle password policy response controls? AD?
-  // TODO: provide alternative cfg for search password.
   // TODO: custom aliveness pings
   // TODO: manage account lockout
   // TODO: cache password
@@ -1926,6 +1928,22 @@
     {
       this.cfg = cfg;
 
+      // First obtain the mapped search password if needed, ignoring any errors
+      // since these should have already been detected during configuration
+      // validation.
+      final String mappedSearchPassword;
+      if (cfg.getMappingPolicy() == MappingPolicy.MAPPED_SEARCH
+          && cfg.getMappedSearchBindDN() != null
+          && !cfg.getMappedSearchBindDN().isNullDN())
+      {
+        mappedSearchPassword = getMappedSearchBindPassword(cfg,
+            new LinkedList<Message>());
+      }
+      else
+      {
+        mappedSearchPassword = null;
+      }
+
       // Use two pools per server: one for authentication (bind) and one for
       // searches. Even if the searches are performed anonymously we cannot use
       // the same pool, otherwise they will be performed as the most recently
@@ -1947,7 +1965,7 @@
         searchPool[index] = new ConnectionPool(
             new AuthenticatedConnectionFactory(factory,
                 cfg.getMappedSearchBindDN(),
-                cfg.getMappedSearchBindPassword()));
+                mappedSearchPassword));
         bindPool[index++] = new ConnectionPool(factory);
       }
       primarySearchLoadBalancer = new RoundRobinLoadBalancer(searchPool,
@@ -1972,7 +1990,7 @@
           searchPool[index] = new ConnectionPool(
               new AuthenticatedConnectionFactory(factory,
                   cfg.getMappedSearchBindDN(),
-                  cfg.getMappedSearchBindPassword()));
+                  mappedSearchPassword));
           bindPool[index++] = new ConnectionPool(factory);
         }
         final RoundRobinLoadBalancer secondarySearchLoadBalancer =
@@ -2098,6 +2116,100 @@
 
 
 
+//Get the search bind password performing mapped searches.
+  //
+  // We will offer several places to look for the password, and we will
+  // do so in the following order:
+  //
+  // - In a specified Java property
+  // - In a specified environment variable
+  // - In a specified file on the server filesystem.
+  // - As the value of a configuration attribute.
+  //
+  // In any case, the password must be in the clear.
+  private static String getMappedSearchBindPassword(
+      final LDAPPassThroughAuthenticationPolicyCfg cfg,
+      final List<Message> unacceptableReasons)
+  {
+    String password = null;
+
+    if (cfg.getMappedSearchBindPasswordProperty() != null)
+    {
+      String propertyName = cfg.getMappedSearchBindPasswordProperty();
+      password = System.getProperty(propertyName);
+      if (password == null)
+      {
+        unacceptableReasons.add(ERR_LDAP_PTA_PWD_PROPERTY_NOT_SET.get(
+            String.valueOf(cfg.dn()), String.valueOf(propertyName)));
+      }
+    }
+    else if (cfg.getMappedSearchBindPasswordEnvironmentVariable() != null)
+    {
+      String envVarName = cfg.getMappedSearchBindPasswordEnvironmentVariable();
+      password = System.getenv(envVarName);
+      if (password == null)
+      {
+        unacceptableReasons.add(ERR_LDAP_PTA_PWD_ENVAR_NOT_SET.get(
+            String.valueOf(cfg.dn()), String.valueOf(envVarName)));
+      }
+    }
+    else if (cfg.getMappedSearchBindPasswordFile() != null)
+    {
+      String fileName = cfg.getMappedSearchBindPasswordFile();
+      File passwordFile = getFileForPath(fileName);
+      if (!passwordFile.exists())
+      {
+        unacceptableReasons.add(ERR_LDAP_PTA_PWD_NO_SUCH_FILE.get(
+            String.valueOf(cfg.dn()), String.valueOf(fileName)));
+      }
+      else
+      {
+        BufferedReader br = null;
+        try
+        {
+          br = new BufferedReader(new FileReader(passwordFile));
+          password = br.readLine();
+          if (password == null)
+          {
+            unacceptableReasons.add(ERR_LDAP_PTA_PWD_FILE_EMPTY.get(
+                String.valueOf(cfg.dn()), String.valueOf(fileName)));
+          }
+        }
+        catch (IOException e)
+        {
+          unacceptableReasons.add(ERR_LDAP_PTA_PWD_FILE_CANNOT_READ.get(
+              String.valueOf(cfg.dn()), String.valueOf(fileName),
+              getExceptionMessage(e)));
+        }
+        finally
+        {
+          try
+          {
+            br.close();
+          }
+          catch (Exception e)
+          {
+            // Ignored.
+          }
+        }
+      }
+    }
+    else if (cfg.getMappedSearchBindPassword() != null)
+    {
+      password = cfg.getMappedSearchBindPassword();
+    }
+    else
+    {
+      // Password wasn't defined anywhere.
+      unacceptableReasons
+          .add(ERR_LDAP_PTA_NO_PWD.get(String.valueOf(cfg.dn())));
+    }
+
+    return password;
+  }
+
+
+
   private static boolean isServerAddressValid(
       final LDAPPassThroughAuthenticationPolicyCfg configuration,
       final List<Message> unacceptableReasons, final String hostPort)
@@ -2189,7 +2301,7 @@
    */
   @Override
   public boolean isConfigurationAcceptable(
-      final LDAPPassThroughAuthenticationPolicyCfg configuration,
+      final LDAPPassThroughAuthenticationPolicyCfg cfg,
       final List<Message> unacceptableReasons)
   {
     // Check that the port numbers are valid. We won't actually try and connect
@@ -2197,18 +2309,29 @@
     // capabilities).
     boolean configurationIsAcceptable = true;
 
-    for (final String hostPort : configuration.getPrimaryRemoteLDAPServer())
+    for (final String hostPort : cfg.getPrimaryRemoteLDAPServer())
     {
-      configurationIsAcceptable &= isServerAddressValid(configuration,
+      configurationIsAcceptable &= isServerAddressValid(cfg,
           unacceptableReasons, hostPort);
     }
 
-    for (final String hostPort : configuration.getSecondaryRemoteLDAPServer())
+    for (final String hostPort : cfg.getSecondaryRemoteLDAPServer())
     {
-      configurationIsAcceptable &= isServerAddressValid(configuration,
+      configurationIsAcceptable &= isServerAddressValid(cfg,
           unacceptableReasons, hostPort);
     }
 
+    // Ensure that the search bind password is defined somewhere.
+    if (cfg.getMappingPolicy() == MappingPolicy.MAPPED_SEARCH
+        && cfg.getMappedSearchBindDN() != null
+        && !cfg.getMappedSearchBindDN().isNullDN())
+    {
+      if (getMappedSearchBindPassword(cfg, unacceptableReasons) == null)
+      {
+        configurationIsAcceptable = false;
+      }
+    }
+
     return configurationIsAcceptable;
   }
 }
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
index e750ecb..d1f57da 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
@@ -424,6 +424,12 @@
     private final SortedSet<String> primaryServers = new TreeSet<String>();
     private final SortedSet<String> secondaryServers = new TreeSet<String>();
     private int timeoutMS = 0; // unlimited
+    private DN mappedSearchBindDN = searchBindDN;
+    private String mappedSearchBindPassword = "searchPassword";
+    private String mappedSearchBindPasswordEnvVar = null;
+    private String mappedSearchBindPasswordFile = null;
+    private String mappedSearchBindPasswordProperty = null;
+
 
 
 
@@ -496,7 +502,7 @@
     @Override
     public DN getMappedSearchBindDN()
     {
-      return searchBindDN;
+      return mappedSearchBindDN;
     }
 
 
@@ -504,7 +510,7 @@
     @Override
     public String getMappedSearchBindPassword()
     {
-      return "searchPassword";
+      return mappedSearchBindPassword;
     }
 
 
@@ -660,6 +666,76 @@
       secondaryServers.add(hostPort);
       return this;
     }
+
+
+
+    MockPolicyCfg withMappedSearchBindDN(final DN value)
+    {
+      this.mappedSearchBindDN = value;
+      return this;
+    }
+
+
+
+    MockPolicyCfg withMappedSearchBindPassword(final String value)
+    {
+      this.mappedSearchBindPassword = value;
+      return this;
+    }
+
+
+
+    MockPolicyCfg withMappedSearchBindPasswordEnvironmentVariable(final String value)
+    {
+      this.mappedSearchBindPasswordEnvVar = value;
+      return this;
+    }
+
+
+
+    MockPolicyCfg withMappedSearchBindPasswordFile(final String value)
+    {
+      this.mappedSearchBindPasswordFile = value;
+      return this;
+    }
+
+
+
+    MockPolicyCfg withMappedSearchBindPasswordProperty(final String value)
+    {
+      this.mappedSearchBindPasswordProperty = value;
+      return this;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getMappedSearchBindPasswordEnvironmentVariable()
+    {
+      return mappedSearchBindPasswordEnvVar;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getMappedSearchBindPasswordFile()
+    {
+      return mappedSearchBindPasswordFile;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getMappedSearchBindPasswordProperty()
+    {
+      return mappedSearchBindPasswordProperty;
+    }
   }
 
 
@@ -2063,6 +2139,8 @@
     // @formatter:off
     return new Object[][] {
         /* cfg, isValid */
+
+        // Test server configuration.
         { mockCfg().withPrimaryServer("test:1"), true },
         { mockCfg().withPrimaryServer("test:65535"), true },
         { mockCfg().withPrimaryServer("test:0"), false },
@@ -2073,6 +2151,17 @@
         { mockCfg().withSecondaryServer("test:0"), false },
         { mockCfg().withSecondaryServer("test:65536"), false },
         { mockCfg().withSecondaryServer("test:1000000"), false },
+
+        // Test mapped search parameters.
+        { mockCfg().withMappingPolicy(MappingPolicy.MAPPED_SEARCH), true },
+        { mockCfg().withMappingPolicy(MappingPolicy.MAPPED_SEARCH).withMappedSearchBindDN(null).withMappedSearchBindPassword(null), true },
+        { mockCfg().withMappingPolicy(MappingPolicy.MAPPED_SEARCH).withMappedSearchBindPassword(null), false },
+        { mockCfg().withMappingPolicy(MappingPolicy.MAPPED_SEARCH).withMappedSearchBindPasswordProperty("org.opendj.dummy.property"), false },
+        { mockCfg().withMappingPolicy(MappingPolicy.MAPPED_SEARCH).withMappedSearchBindPasswordProperty("java.version"), true },
+        { mockCfg().withMappingPolicy(MappingPolicy.MAPPED_SEARCH).withMappedSearchBindPasswordEnvironmentVariable("ORG_OPENDJ_DUMMY_ENVVAR"), false },
+        { mockCfg().withMappingPolicy(MappingPolicy.MAPPED_SEARCH).withMappedSearchBindPasswordFile("dummy_file.txt"), false },
+        { mockCfg().withMappingPolicy(MappingPolicy.MAPPED_SEARCH).withMappedSearchBindPasswordFile("config/admin-keystore.pin"), true },
+
     };
     // @formatter:on
   }

--
Gitblit v1.10.0