001/*
002 * Copyright 2011-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2011-2020 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2011-2020 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.listener;
037
038
039
040import java.util.ArrayList;
041import java.util.Arrays;
042import java.util.Collection;
043import java.util.Collections;
044import java.util.EnumSet;
045import java.util.HashSet;
046import java.util.Iterator;
047import java.util.LinkedHashMap;
048import java.util.LinkedHashSet;
049import java.util.List;
050import java.util.Map;
051import java.util.Set;
052import java.util.logging.Handler;
053
054import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
055import com.unboundid.ldap.sdk.DN;
056import com.unboundid.ldap.sdk.Entry;
057import com.unboundid.ldap.sdk.LDAPException;
058import com.unboundid.ldap.sdk.OperationType;
059import com.unboundid.ldap.sdk.ReadOnlyEntry;
060import com.unboundid.ldap.sdk.ResultCode;
061import com.unboundid.ldap.sdk.Version;
062import com.unboundid.ldap.sdk.schema.Schema;
063import com.unboundid.util.Mutable;
064import com.unboundid.util.NotExtensible;
065import com.unboundid.util.StaticUtils;
066import com.unboundid.util.ThreadSafety;
067import com.unboundid.util.ThreadSafetyLevel;
068
069import static com.unboundid.ldap.listener.ListenerMessages.*;
070
071
072
073/**
074 * This class provides a simple data structure with information that may be
075 * used to control the behavior of an {@link InMemoryDirectoryServer} instance.
076 * At least one base DN must be specified.  For all other properties, the
077 * following default values will be used unless an alternate configuration is
078 * provided:
079 * <UL>
080 *   <LI>Listeners:  The server will provide a single listener that will use an
081 *       automatically-selected port on all interfaces, which will not use SSL
082 *       or StartTLS.</LI>
083 *   <LI>Allowed Operation Types:  All types of operations will be allowed.</LI>
084 *   <LI>Authentication Required Operation Types:  Authentication will not be
085 *       required for any types of operations.</LI>
086 *   <LI>Schema:  The server will use a schema with a number of standard
087 *       attribute types and object classes.</LI>
088 *   <LI>Additional Bind Credentials:  The server will not have any additional
089 *       bind credentials.</LI>
090 *   <LI>Referential Integrity Attributes:  Referential integrity will not be
091 *       maintained.</LI>
092 *   <LI>Generate Operational Attributes:  The server will automatically
093 *       generate a number of operational attributes.</LI>
094 *   <LI>Extended Operation Handlers:  The server will support the password
095 *       modify extended operation as defined in RFC 3062, the start and end
096 *       transaction extended operations as defined in RFC 5805, and the
097 *       "Who Am I?" extended operation as defined in RFC 4532.</LI>
098 *   <LI>SASL Bind Handlers:  The server will support the SASL PLAIN mechanism
099 *       as defined in RFC 4616.</LI>
100 *   <LI>Max ChangeLog Entries:  The server will not provide an LDAP
101 *       changelog.</LI>
102 *   <LI>Access Log Handler:  The server will not perform any access
103 *       logging.</LI>
104 *   <LI>Code Log Handler:  The server will not perform any code logging.</LI>
105 *   <LI>LDAP Debug Log Handler:  The server will not perform any LDAP debug
106 *       logging.</LI>
107 *   <LI>Listener Exception Handler:  The server will not use a listener
108 *       exception handler.</LI>
109 *   <LI>Maximum Size Limit:  The server will not enforce a maximum search size
110 *       limit.</LI>
111 *   <LI>Password Attributes:  The server will use userPassword as the only
112 *       password attribute.</LI>
113 *   <LI>Password Encoders:  The server will not use any password encoders by
114 *       default, so passwords will remain in clear text.</LI>
115 * </UL>
116 */
117@NotExtensible()
118@Mutable()
119@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
120public class InMemoryDirectoryServerConfig
121{
122  // Indicates whether to enforce the requirement that attribute values comply
123  // with the associated attribute syntax.
124  private boolean enforceAttributeSyntaxCompliance;
125
126  // Indicates whether to enforce the requirement that entries contain exactly
127  // one structural object class.
128  private boolean enforceSingleStructuralObjectClass;
129
130  // Indicates whether to automatically generate operational attributes.
131  private boolean generateOperationalAttributes;
132
133  // Indicates whether the code log should include sample code for processing
134  // the requests.
135  private boolean includeRequestProcessingInCodeLog;
136
137  // The base DNs to use for the LDAP listener.
138  private DN[] baseDNs;
139
140  // The log handler that should be used to record access log messages about
141  // operations processed by the server.
142  private Handler accessLogHandler;
143
144  // The log handler that should be used to record JSON-formatted access log
145  // messages about operations processed by the server.
146  private Handler jsonAccessLogHandler;
147
148  // The log handler that should be used to record detailed protocol-level
149  // messages about LDAP operations processed by the server.
150  private Handler ldapDebugLogHandler;
151
152  // The password encoder that will be used to encode new clear-text passwords.
153  private InMemoryPasswordEncoder primaryPasswordEncoder;
154
155  // The maximum number of entries to retain in a generated changelog.
156  private int maxChangeLogEntries;
157
158  // The maximum number of concurrent connections that will be allowed.
159  private int maxConnections;
160
161  // The maximum number of entries that may be returned in any single search
162  // operation.
163  private int maxSizeLimit;
164
165  // The exception handler that should be used for the listener.
166  private LDAPListenerExceptionHandler exceptionHandler;
167
168  // The extended operation handlers that may be used to process extended
169  // operations in the server.
170  private final List<InMemoryExtendedOperationHandler>
171       extendedOperationHandlers;
172
173  // The listener configurations that should be used for accepting connections
174  // to the server.
175  private final List<InMemoryListenerConfig> listenerConfigs;
176
177  // The operation interceptors that should be used with the in-memory directory
178  // server.
179  private final List<InMemoryOperationInterceptor> operationInterceptors;
180
181  // A list of secondary password encoders that will be used to interact with
182  // existing pre-encoded passwords, but will not be used to encode new
183  // passwords.
184  private final List<InMemoryPasswordEncoder> secondaryPasswordEncoders;
185
186  // The SASL bind handlers that may be used to process SASL bind requests in
187  // the server.
188  private final List<InMemorySASLBindHandler> saslBindHandlers;
189
190  // The names or OIDs of the attributes for which to maintain equality indexes.
191  private final List<String> equalityIndexAttributes;
192
193  // A set of additional credentials that can be used for binding without
194  // requiring a corresponding entry in the data set.
195  private final Map<DN,byte[]> additionalBindCredentials;
196
197  // The entry to use for the server root DSE.
198  private ReadOnlyEntry rootDSEEntry;
199
200  // The schema to use for the server.
201  private Schema schema;
202
203  // The set of operation types that will be supported by the server.
204  private final Set<OperationType> allowedOperationTypes;
205
206  // The set of operation types for which authentication will be required.
207  private final Set<OperationType> authenticationRequiredOperationTypes;
208
209  // The set of attributes for which referential integrity should be maintained.
210  private final Set<String> referentialIntegrityAttributes;
211
212  // The set of attributes that will hold user passwords.
213  private final Set<String> passwordAttributes;
214
215  // The path to a file that should be written with code that may be used to
216  // issue the requests received by the server.
217  private String codeLogPath;
218
219  // The vendor name to report in the server root DSE.
220  private String vendorName;
221
222  // The vendor version to report in the server root DSE.
223  private String vendorVersion;
224
225
226
227  /**
228   * Creates a new in-memory directory server config object with the provided
229   * set of base DNs.
230   *
231   * @param  baseDNs  The set of base DNs to use for the server.  It must not
232   *                  be {@code null} or empty.
233   *
234   * @throws  LDAPException  If the provided set of base DN strings is null or
235   *                         empty, or if any of the provided base DN strings
236   *                         cannot be parsed as a valid DN.
237   */
238  public InMemoryDirectoryServerConfig(final String... baseDNs)
239         throws LDAPException
240  {
241    this(parseDNs(Schema.getDefaultStandardSchema(), baseDNs));
242  }
243
244
245
246  /**
247   * Creates a new in-memory directory server config object with the default
248   * settings.
249   *
250   * @param  baseDNs  The set of base DNs to use for the server.  It must not
251   *                  be {@code null} or empty.
252   *
253   * @throws  LDAPException  If the provided set of base DNs is null or empty.
254   */
255  public InMemoryDirectoryServerConfig(final DN... baseDNs)
256         throws LDAPException
257  {
258    if ((baseDNs == null) || (baseDNs.length == 0))
259    {
260      throw new LDAPException(ResultCode.PARAM_ERROR,
261           ERR_MEM_DS_CFG_NO_BASE_DNS.get());
262    }
263
264    this.baseDNs = baseDNs;
265
266    listenerConfigs = new ArrayList<>(1);
267    listenerConfigs.add(InMemoryListenerConfig.createLDAPConfig("default"));
268
269    additionalBindCredentials            =
270         new LinkedHashMap<>(StaticUtils.computeMapCapacity(1));
271    accessLogHandler                     = null;
272    jsonAccessLogHandler                 = null;
273    ldapDebugLogHandler                  = null;
274    enforceAttributeSyntaxCompliance     = true;
275    enforceSingleStructuralObjectClass   = true;
276    generateOperationalAttributes        = true;
277    maxChangeLogEntries                  = 0;
278    maxConnections                       = 0;
279    maxSizeLimit                         = 0;
280    exceptionHandler                     = null;
281    equalityIndexAttributes              = new ArrayList<>(10);
282    rootDSEEntry                         = null;
283    schema                               = Schema.getDefaultStandardSchema();
284    allowedOperationTypes                = EnumSet.allOf(OperationType.class);
285    authenticationRequiredOperationTypes = EnumSet.noneOf(OperationType.class);
286    referentialIntegrityAttributes       = new HashSet<>(0);
287    vendorName                           = "Ping Identity Corporation";
288    vendorVersion                        = Version.FULL_VERSION_STRING;
289    codeLogPath                          = null;
290    includeRequestProcessingInCodeLog    = false;
291
292    operationInterceptors = new ArrayList<>(5);
293
294    extendedOperationHandlers = new ArrayList<>(3);
295    extendedOperationHandlers.add(new PasswordModifyExtendedOperationHandler());
296    extendedOperationHandlers.add(new TransactionExtendedOperationHandler());
297    extendedOperationHandlers.add(new WhoAmIExtendedOperationHandler());
298
299    saslBindHandlers = new ArrayList<>(1);
300    saslBindHandlers.add(new PLAINBindHandler());
301
302    passwordAttributes = new LinkedHashSet<>(StaticUtils.computeMapCapacity(5));
303    passwordAttributes.add("userPassword");
304
305    primaryPasswordEncoder = null;
306
307    secondaryPasswordEncoders = new ArrayList<>(5);
308  }
309
310
311
312  /**
313   * Creates a new in-memory directory server config object that is a duplicate
314   * of the provided config and may be altered without impacting the state of
315   * the given config object.
316   *
317   * @param  cfg  The in-memory directory server config object for to be
318   *              duplicated.
319   */
320  public InMemoryDirectoryServerConfig(final InMemoryDirectoryServerConfig cfg)
321  {
322    baseDNs = new DN[cfg.baseDNs.length];
323    System.arraycopy(cfg.baseDNs, 0, baseDNs, 0, baseDNs.length);
324
325    listenerConfigs = new ArrayList<>(cfg.listenerConfigs);
326
327    operationInterceptors = new ArrayList<>(cfg.operationInterceptors);
328
329    extendedOperationHandlers = new ArrayList<>(cfg.extendedOperationHandlers);
330
331    saslBindHandlers = new ArrayList<>(cfg.saslBindHandlers);
332
333    additionalBindCredentials =
334         new LinkedHashMap<>(cfg.additionalBindCredentials);
335
336    referentialIntegrityAttributes =
337         new HashSet<>(cfg.referentialIntegrityAttributes);
338
339    allowedOperationTypes = EnumSet.noneOf(OperationType.class);
340    allowedOperationTypes.addAll(cfg.allowedOperationTypes);
341
342    authenticationRequiredOperationTypes = EnumSet.noneOf(OperationType.class);
343    authenticationRequiredOperationTypes.addAll(
344         cfg.authenticationRequiredOperationTypes);
345
346    equalityIndexAttributes = new ArrayList<>(cfg.equalityIndexAttributes);
347
348    enforceAttributeSyntaxCompliance   = cfg.enforceAttributeSyntaxCompliance;
349    enforceSingleStructuralObjectClass = cfg.enforceSingleStructuralObjectClass;
350    generateOperationalAttributes      = cfg.generateOperationalAttributes;
351    accessLogHandler                   = cfg.accessLogHandler;
352    jsonAccessLogHandler               = cfg.jsonAccessLogHandler;
353    ldapDebugLogHandler                = cfg.ldapDebugLogHandler;
354    maxChangeLogEntries                = cfg.maxChangeLogEntries;
355    maxConnections                     = cfg.maxConnections;
356    maxSizeLimit                       = cfg.maxSizeLimit;
357    exceptionHandler                   = cfg.exceptionHandler;
358    rootDSEEntry                       = cfg.rootDSEEntry;
359    schema                             = cfg.schema;
360    vendorName                         = cfg.vendorName;
361    vendorVersion                      = cfg.vendorVersion;
362    codeLogPath                        = cfg.codeLogPath;
363    includeRequestProcessingInCodeLog  = cfg.includeRequestProcessingInCodeLog;
364    primaryPasswordEncoder             = cfg.primaryPasswordEncoder;
365
366    passwordAttributes = new LinkedHashSet<>(cfg.passwordAttributes);
367
368    secondaryPasswordEncoders = new ArrayList<>(cfg.secondaryPasswordEncoders);
369  }
370
371
372
373  /**
374   * Retrieves the set of base DNs that should be used for the directory server.
375   *
376   * @return  The set of base DNs that should be used for the directory server.
377   */
378  public DN[] getBaseDNs()
379  {
380    return baseDNs;
381  }
382
383
384
385  /**
386   * Specifies the set of base DNs that should be used for the directory server.
387   *
388   * @param  baseDNs  The set of base DNs that should be used for the directory
389   *                  server.  It must not be {@code null} or empty.
390   *
391   * @throws  LDAPException  If the provided set of base DN strings is null or
392   *                         empty, or if any of the provided base DN strings
393   *                         cannot be parsed as a valid DN.
394   */
395  public void setBaseDNs(final String... baseDNs)
396         throws LDAPException
397  {
398    setBaseDNs(parseDNs(schema, baseDNs));
399  }
400
401
402
403  /**
404   * Specifies the set of base DNs that should be used for the directory server.
405   *
406   * @param  baseDNs  The set of base DNs that should be used for the directory
407   *                  server.  It must not be {@code null} or empty.
408   *
409   * @throws  LDAPException  If the provided set of base DNs is null or empty.
410   */
411  public void setBaseDNs(final DN... baseDNs)
412         throws LDAPException
413  {
414    if ((baseDNs == null) || (baseDNs.length == 0))
415    {
416      throw new LDAPException(ResultCode.PARAM_ERROR,
417           ERR_MEM_DS_CFG_NO_BASE_DNS.get());
418    }
419
420    this.baseDNs = baseDNs;
421  }
422
423
424
425  /**
426   * Retrieves the list of listener configurations that should be used for the
427   * directory server.
428   *
429   * @return  The list of listener configurations that should be used for the
430   *          directory server.
431   */
432  public List<InMemoryListenerConfig> getListenerConfigs()
433  {
434    return listenerConfigs;
435  }
436
437
438
439  /**
440   * Specifies the configurations for all listeners that should be used for the
441   * directory server.
442   *
443   * @param  listenerConfigs  The configurations for all listeners that should
444   *                          be used for the directory server.  It must not be
445   *                          {@code null} or empty, and it must not contain
446   *                          multiple configurations with the same name.
447   *
448   * @throws  LDAPException  If there is a problem with the provided set of
449   *                         listener configurations.
450   */
451  public void setListenerConfigs(
452                   final InMemoryListenerConfig... listenerConfigs)
453         throws LDAPException
454  {
455    setListenerConfigs(StaticUtils.toList(listenerConfigs));
456  }
457
458
459
460  /**
461   * Specifies the configurations for all listeners that should be used for the
462   * directory server.
463   *
464   * @param  listenerConfigs  The configurations for all listeners that should
465   *                          be used for the directory server.  It must not be
466   *                          {@code null} or empty, and it must not contain
467   *                          multiple configurations with the same name.
468   *
469   * @throws  LDAPException  If there is a problem with the provided set of
470   *                         listener configurations.
471   */
472  public void setListenerConfigs(
473                   final Collection<InMemoryListenerConfig> listenerConfigs)
474         throws LDAPException
475  {
476    if ((listenerConfigs == null) || listenerConfigs.isEmpty())
477    {
478      throw new LDAPException(ResultCode.PARAM_ERROR,
479           ERR_MEM_DS_CFG_NO_LISTENERS.get());
480    }
481
482    final HashSet<String> listenerNames =
483         new HashSet<>(StaticUtils.computeMapCapacity(listenerConfigs.size()));
484    for (final InMemoryListenerConfig c : listenerConfigs)
485    {
486      final String name = StaticUtils.toLowerCase(c.getListenerName());
487      if (listenerNames.contains(name))
488      {
489        throw new LDAPException(ResultCode.PARAM_ERROR,
490             ERR_MEM_DS_CFG_CONFLICTING_LISTENER_NAMES.get(name));
491      }
492      else
493      {
494        listenerNames.add(name);
495      }
496    }
497
498    this.listenerConfigs.clear();
499    this.listenerConfigs.addAll(listenerConfigs);
500  }
501
502
503
504  /**
505   * Retrieves the set of operation types that will be allowed by the server.
506   * Note that if the server is configured to support StartTLS, then it will be
507   * allowed even if other types of extended operations are not allowed.
508   *
509   * @return  The set of operation types that will be allowed by the server.
510   */
511  public Set<OperationType> getAllowedOperationTypes()
512  {
513    return allowedOperationTypes;
514  }
515
516
517
518  /**
519   * Specifies the set of operation types that will be allowed by the server.
520   * Note that if the server is configured to support StartTLS, then it will be
521   * allowed even if other types of extended operations are not allowed.
522   *
523   * @param  operationTypes  The set of operation types that will be allowed by
524   *                         the server.
525   */
526  public void setAllowedOperationTypes(final OperationType... operationTypes)
527  {
528    allowedOperationTypes.clear();
529    if (operationTypes != null)
530    {
531      allowedOperationTypes.addAll(Arrays.asList(operationTypes));
532    }
533  }
534
535
536
537  /**
538   * Specifies the set of operation types that will be allowed by the server.
539   * Note that if the server is configured to support StartTLS, then it will be
540   * allowed even if other types of extended operations are not allowed.
541   *
542   * @param  operationTypes  The set of operation types that will be allowed by
543   *                         the server.
544   */
545  public void setAllowedOperationTypes(
546                   final Collection<OperationType> operationTypes)
547  {
548    allowedOperationTypes.clear();
549    if (operationTypes != null)
550    {
551      allowedOperationTypes.addAll(operationTypes);
552    }
553  }
554
555
556
557  /**
558   * Retrieves the set of operation types that will only be allowed for
559   * authenticated clients.  Note that authentication will never be required for
560   * bind operations, and if the server is configured to support StartTLS, then
561   * authentication will never be required for StartTLS operations even if it
562   * is required for other types of extended operations.
563   *
564   * @return  The set of operation types that will only be allowed for
565   *          authenticated clients.
566   */
567  public Set<OperationType> getAuthenticationRequiredOperationTypes()
568  {
569    return authenticationRequiredOperationTypes;
570  }
571
572
573
574  /**
575   * Specifies the set of operation types that will only be allowed for
576   * authenticated clients.  Note that authentication will never be required for
577   * bind operations, and if the server is configured to support StartTLS, then
578   * authentication will never be required for StartTLS operations even if it
579   * is required for other types of extended operations.
580   *
581   * @param  operationTypes  The set of operation types that will be allowed for
582   *                         authenticated clients.
583   */
584  public void setAuthenticationRequiredOperationTypes(
585                   final OperationType... operationTypes)
586  {
587    authenticationRequiredOperationTypes.clear();
588    if (operationTypes != null)
589    {
590      authenticationRequiredOperationTypes.addAll(
591           Arrays.asList(operationTypes));
592    }
593  }
594
595
596
597  /**
598   * Specifies the set of operation types that will only be allowed for
599   * authenticated clients.  Note that authentication will never be required for
600   * bind operations, and if the server is configured to support StartTLS, then
601   * authentication will never be required for StartTLS operations even if it
602   * is required for other types of extended operations.
603   *
604   * @param  operationTypes  The set of operation types that will be allowed for
605   *                         authenticated clients.
606   */
607  public void setAuthenticationRequiredOperationTypes(
608                   final Collection<OperationType> operationTypes)
609  {
610    authenticationRequiredOperationTypes.clear();
611    if (operationTypes != null)
612    {
613      authenticationRequiredOperationTypes.addAll(operationTypes);
614    }
615  }
616
617
618
619  /**
620   * Retrieves a map containing DNs and passwords of additional users that will
621   * be allowed to bind to the server, even if their entries do not exist in the
622   * data set.  This can be used to mimic the functionality of special
623   * administrative accounts (e.g., "cn=Directory Manager" in many directories).
624   * The map that is returned may be altered if desired.
625   *
626   * @return  A map containing DNs and passwords of additional users that will
627   *          be allowed to bind to the server, even if their entries do not
628   *          exist in the data set.
629   */
630  public Map<DN,byte[]> getAdditionalBindCredentials()
631  {
632    return additionalBindCredentials;
633  }
634
635
636
637  /**
638   * Adds an additional bind DN and password combination that can be used to
639   * bind to the server, even if the corresponding entry does not exist in the
640   * data set.  This can be used to mimic the functionality of special
641   * administrative accounts (e.g., "cn=Directory Manager" in many directories).
642   * If a password has already been defined for the given DN, then it will be
643   * replaced with the newly-supplied password.
644   *
645   * @param  dn        The bind DN to allow.  It must not be {@code null} or
646   *                   represent the null DN.
647   * @param  password  The password for the provided bind DN.  It must not be
648   *                   {@code null} or empty.
649   *
650   * @throws  LDAPException  If there is a problem with the provided bind DN or
651   *                         password.
652   */
653  public void addAdditionalBindCredentials(final String dn,
654                                           final String password)
655         throws LDAPException
656  {
657    addAdditionalBindCredentials(dn, StaticUtils.getBytes(password));
658  }
659
660
661
662  /**
663   * Adds an additional bind DN and password combination that can be used to
664   * bind to the server, even if the corresponding entry does not exist in the
665   * data set.  This can be used to mimic the functionality of special
666   * administrative accounts (e.g., "cn=Directory Manager" in many directories).
667   * If a password has already been defined for the given DN, then it will be
668   * replaced with the newly-supplied password.
669   *
670   * @param  dn        The bind DN to allow.  It must not be {@code null} or
671   *                   represent the null DN.
672   * @param  password  The password for the provided bind DN.  It must not be
673   *                   {@code null} or empty.
674   *
675   * @throws  LDAPException  If there is a problem with the provided bind DN or
676   *                         password.
677   */
678  public void addAdditionalBindCredentials(final String dn,
679                                           final byte[] password)
680         throws LDAPException
681  {
682    if (dn == null)
683    {
684      throw new LDAPException(ResultCode.PARAM_ERROR,
685           ERR_MEM_DS_CFG_NULL_ADDITIONAL_BIND_DN.get());
686    }
687
688    final DN parsedDN = new DN(dn, schema);
689    if (parsedDN.isNullDN())
690    {
691      throw new LDAPException(ResultCode.PARAM_ERROR,
692           ERR_MEM_DS_CFG_NULL_ADDITIONAL_BIND_DN.get());
693    }
694
695    if ((password == null) || (password.length == 0))
696    {
697      throw new LDAPException(ResultCode.PARAM_ERROR,
698           ERR_MEM_DS_CFG_NULL_ADDITIONAL_BIND_PW.get());
699    }
700
701    additionalBindCredentials.put(parsedDN, password);
702  }
703
704
705
706  /**
707   * Retrieves the object that should be used to handle any errors encountered
708   * while attempting to interact with a client, if defined.
709   *
710   * @return  The object that should be used to handle any errors encountered
711   *          while attempting to interact with a client, or {@code null} if no
712   *          exception handler should be used.
713   */
714  public LDAPListenerExceptionHandler getListenerExceptionHandler()
715  {
716    return exceptionHandler;
717  }
718
719
720
721  /**
722   * Specifies the LDAP listener exception handler that the server should use to
723   * handle any errors encountered while attempting to interact with a client.
724   *
725   * @param  exceptionHandler  The LDAP listener exception handler that the
726   *                           server should use to handle any errors
727   *                           encountered while attempting to interact with a
728   *                           client.  It may be {@code null} if no exception
729   *                           handler should be used.
730   */
731  public void setListenerExceptionHandler(
732                   final LDAPListenerExceptionHandler exceptionHandler)
733  {
734    this.exceptionHandler = exceptionHandler;
735  }
736
737
738
739  /**
740   * Retrieves the schema that should be used by the server, if defined.  If a
741   * schema is defined, then it will be used to validate entries and determine
742   * which matching rules should be used for various types of matching
743   * operations.
744   *
745   * @return  The schema that should be used by the server, or {@code null} if
746   *          no schema should be used.
747   */
748  public Schema getSchema()
749  {
750    return schema;
751  }
752
753
754
755  /**
756   * Specifies the schema that should be used by the server.  If a schema is
757   * defined, then it will be used to validate entries and determine which
758   * matching rules should be used for various types of matching operations.
759   *
760   * @param  schema  The schema that should be used by the server.  It may be
761   *                 {@code null} if no schema should be used.
762   */
763  public void setSchema(final Schema schema)
764  {
765    this.schema = schema;
766  }
767
768
769
770  /**
771   * Indicates whether the server should reject attribute values which violate
772   * the constraints of the associated syntax.  This setting will be ignored if
773   * a {@code null} schema is in place.
774   *
775   * @return  {@code true} if the server should reject attribute values which
776   *          violate the constraints of the associated syntax, or {@code false}
777   *          if not.
778   */
779  public boolean enforceAttributeSyntaxCompliance()
780  {
781    return enforceAttributeSyntaxCompliance;
782  }
783
784
785
786  /**
787   * Specifies whether the server should reject attribute values which violate
788   * the constraints of the associated syntax.  This setting will be ignored if
789   * a {@code null} schema is in place.
790   *
791   * @param  enforceAttributeSyntaxCompliance  Indicates whether the server
792   *                                           should reject attribute values
793   *                                           which violate the constraints of
794   *                                           the associated syntax.
795   */
796  public void setEnforceAttributeSyntaxCompliance(
797                   final boolean enforceAttributeSyntaxCompliance)
798  {
799    this.enforceAttributeSyntaxCompliance = enforceAttributeSyntaxCompliance;
800  }
801
802
803
804  /**
805   * Indicates whether the server should reject entries which do not contain
806   * exactly one structural object class.  This setting will be ignored if a
807   * {@code null} schema is in place.
808   *
809   * @return  {@code true} if the server should reject entries which do not
810   *          contain exactly one structural object class, or {@code false} if
811   *          it should allow entries which do not have any structural class or
812   *          that have multiple structural classes.
813   */
814  public boolean enforceSingleStructuralObjectClass()
815  {
816    return enforceSingleStructuralObjectClass;
817  }
818
819
820
821  /**
822   * Specifies whether the server should reject entries which do not contain
823   * exactly one structural object class.  This setting will be ignored if a
824   * {@code null} schema is in place.
825   *
826   * @param  enforceSingleStructuralObjectClass  Indicates whether the server
827   *                                             should reject entries which do
828   *                                             not contain exactly one
829   *                                             structural object class.
830   */
831  public void setEnforceSingleStructuralObjectClass(
832                   final boolean enforceSingleStructuralObjectClass)
833  {
834    this.enforceSingleStructuralObjectClass =
835         enforceSingleStructuralObjectClass;
836  }
837
838
839
840  /**
841   * Retrieves the log handler that should be used to record access log messages
842   * about operations processed by the server, if any.
843   *
844   * @return  The log handler that should be used to record access log messages
845   *          about operations processed by the server, or {@code null} if no
846   *          access logging should be performed.
847   */
848  public Handler getAccessLogHandler()
849  {
850    return accessLogHandler;
851  }
852
853
854
855  /**
856   * Specifies the log handler that should be used to record access log messages
857   * about operations processed by the server.
858   *
859   * @param  accessLogHandler  The log handler that should be used to record
860   *                           access log messages about operations processed by
861   *                           the server.  It may be {@code null} if no access
862   *                           logging should be performed.
863   */
864  public void setAccessLogHandler(final Handler accessLogHandler)
865  {
866    this.accessLogHandler = accessLogHandler;
867  }
868
869
870
871  /**
872   * Retrieves the log handler that should be used to record JSON-formatted
873   * access log messages about operations processed by the server, if any.
874   *
875   * @return  The log handler that should be used to record JSON-formatted
876   *          access log messages about operations processed by the server, or
877   *          {@code null} if no access logging should be performed.
878   */
879  public Handler getJSONAccessLogHandler()
880  {
881    return jsonAccessLogHandler;
882  }
883
884
885
886  /**
887   * Specifies the log handler that should be used to record JSON-formatted
888   * access log messages about operations processed by the server.
889   *
890   * @param  jsonAccessLogHandler  The log handler that should be used to record
891   *                               JSON-formatted access log messages about
892   *                               operations processed by the server.  It may
893   *                               be {@code null} if no access logging should
894   *                               be performed.
895   */
896  public void setJSONAccessLogHandler(final Handler jsonAccessLogHandler)
897  {
898    this.jsonAccessLogHandler = jsonAccessLogHandler;
899  }
900
901
902
903  /**
904   * Retrieves the log handler that should be used to record detailed messages
905   * about LDAP communication to and from the server, which may be useful for
906   * debugging purposes.
907   *
908   * @return  The log handler that should be used to record detailed
909   *          protocol-level debug messages about LDAP communication to and from
910   *          the server, or {@code null} if no debug logging should be
911   *          performed.
912   */
913  public Handler getLDAPDebugLogHandler()
914  {
915    return ldapDebugLogHandler;
916  }
917
918
919
920  /**
921   * Specifies the log handler that should be used to record detailed messages
922   * about LDAP communication to and from the server, which may be useful for
923   * debugging purposes.
924   *
925   * @param  ldapDebugLogHandler  The log handler that should be used to record
926   *                              detailed messages about LDAP communication to
927   *                              and from the server.  It may be {@code null}
928   *                              if no LDAP debug logging should be performed.
929   */
930  public void setLDAPDebugLogHandler(final Handler ldapDebugLogHandler)
931  {
932    this.ldapDebugLogHandler = ldapDebugLogHandler;
933  }
934
935
936
937  /**
938   * Retrieves the path to a file to be written with generated code that may
939   * be used to construct the requests processed by the server.
940   *
941   * @return  The path to a file to be written with generated code that may be
942   *          used to construct the requests processed by the server, or
943   *          {@code null} if no code log should be written.
944   */
945  public String getCodeLogPath()
946  {
947    return codeLogPath;
948  }
949
950
951
952  /**
953   * Indicates whether the code log should include sample code for processing
954   * the generated requests.  This will only be used if {@link #getCodeLogPath}
955   * returns a non-{@code null} value.
956   *
957   * @return  {@code false} if the code log should only include code that
958   *          corresponds to requests received from clients, or {@code true} if
959   *          the code log should also include sample code for processing the
960   *          generated requests and interpreting the results.
961   */
962  public boolean includeRequestProcessingInCodeLog()
963  {
964    return includeRequestProcessingInCodeLog;
965  }
966
967
968
969  /**
970   * Specifies information about code logging that should be performed by the
971   * server, if any.
972   *
973   * @param  codeLogPath        The path to the file to which a code log should
974   *                            be written.  It may be {@code null} if no code
975   *                            log should be written.
976   * @param  includeProcessing  Indicates whether to include sample code that
977   *                            demonstrates how to process the requests and
978   *                            interpret the results.  This will only be
979   *                            used if the {@code codeLogPath} argument is
980   *                            non-{@code null}.
981   */
982  public void setCodeLogDetails(final String codeLogPath,
983                                final boolean includeProcessing)
984  {
985    this.codeLogPath = codeLogPath;
986    includeRequestProcessingInCodeLog = includeProcessing;
987  }
988
989
990
991  /**
992   * Retrieves a list of the operation interceptors that may be used to
993   * intercept and transform requests before they are processed by the in-memory
994   * directory server, and/or to intercept and transform responses before they
995   * are returned to the client.  The contents of the list may be altered by the
996   * caller.
997   *
998   * @return  An updatable list of the operation interceptors that may be used
999   *          to intercept and transform requests and/or responses.
1000   */
1001  public List<InMemoryOperationInterceptor> getOperationInterceptors()
1002  {
1003    return operationInterceptors;
1004  }
1005
1006
1007
1008  /**
1009   * Adds the provided operation interceptor to the list of operation
1010   * interceptors that may be used to transform requests before they are
1011   * processed by the in-memory directory server, and/or to transform responses
1012   * before they are returned to the client.
1013   *
1014   * @param  interceptor  The operation interceptor that should be invoked in
1015   *                      the course of processing requests and responses.
1016   */
1017  public void addInMemoryOperationInterceptor(
1018                   final InMemoryOperationInterceptor interceptor)
1019  {
1020    operationInterceptors.add(interceptor);
1021  }
1022
1023
1024
1025  /**
1026   * Retrieves a list of the extended operation handlers that may be used to
1027   * process extended operations in the server.  The contents of the list may
1028   * be altered by the caller.
1029   *
1030   * @return  An updatable list of the extended operation handlers that may be
1031   *          used to process extended operations in the server.
1032   */
1033  public List<InMemoryExtendedOperationHandler> getExtendedOperationHandlers()
1034  {
1035    return extendedOperationHandlers;
1036  }
1037
1038
1039
1040  /**
1041   * Adds the provided extended operation handler for use by the server for
1042   * processing certain types of extended operations.
1043   *
1044   * @param  handler  The extended operation handler that should be used by the
1045   *                  server for processing certain types of extended
1046   *                  operations.
1047   */
1048  public void addExtendedOperationHandler(
1049                   final InMemoryExtendedOperationHandler handler)
1050  {
1051    extendedOperationHandlers.add(handler);
1052  }
1053
1054
1055
1056  /**
1057   * Retrieves a list of the SASL bind handlers that may be used to process
1058   * SASL bind requests in the server.  The contents of the list may be altered
1059   * by the caller.
1060   *
1061   * @return  An updatable list of the SASL bind handlers that may be used to
1062   *          process SASL bind requests in the server.
1063   */
1064  public List<InMemorySASLBindHandler> getSASLBindHandlers()
1065  {
1066    return saslBindHandlers;
1067  }
1068
1069
1070
1071  /**
1072   * Adds the provided SASL bind handler for use by the server for processing
1073   * certain types of SASL bind requests.
1074   *
1075   * @param  handler  The SASL bind handler that should be used by the server
1076   *                  for processing certain types of SASL bind requests.
1077   */
1078  public void addSASLBindHandler(final InMemorySASLBindHandler handler)
1079  {
1080    saslBindHandlers.add(handler);
1081  }
1082
1083
1084
1085  /**
1086   * Indicates whether the server should automatically generate operational
1087   * attributes (including entryDN, entryUUID, creatorsName, createTimestamp,
1088   * modifiersName, modifyTimestamp, and subschemaSubentry) for entries in the
1089   * server.
1090   *
1091   * @return  {@code true} if the server should automatically generate
1092   *          operational attributes for entries in the server, or {@code false}
1093   *          if not.
1094   */
1095  public boolean generateOperationalAttributes()
1096  {
1097    return generateOperationalAttributes;
1098  }
1099
1100
1101
1102  /**
1103   * Specifies whether the server should automatically generate operational
1104   * attributes (including entryDN, entryUUID, creatorsName, createTimestamp,
1105   * modifiersName, modifyTimestamp, and subschemaSubentry) for entries in the
1106   * server.
1107   *
1108   * @param  generateOperationalAttributes  Indicates whether the server should
1109   *                                        automatically generate operational
1110   *                                        attributes for entries in the
1111   *                                        server.
1112   */
1113  public void setGenerateOperationalAttributes(
1114                   final boolean generateOperationalAttributes)
1115  {
1116    this.generateOperationalAttributes = generateOperationalAttributes;
1117  }
1118
1119
1120
1121  /**
1122   * Retrieves the maximum number of changelog entries that the server should
1123   * maintain.
1124   *
1125   * @return  The maximum number of changelog entries that the server should
1126   *          maintain, or 0 if the server should not maintain a changelog.
1127   */
1128  public int getMaxChangeLogEntries()
1129  {
1130    return maxChangeLogEntries;
1131  }
1132
1133
1134
1135  /**
1136   * Specifies the maximum number of changelog entries that the server should
1137   * maintain.  A value less than or equal to zero indicates that the server
1138   * should not attempt to maintain a changelog.
1139   *
1140   * @param  maxChangeLogEntries  The maximum number of changelog entries that
1141   *                              the server should maintain.
1142   */
1143  public void setMaxChangeLogEntries(final int maxChangeLogEntries)
1144  {
1145    if (maxChangeLogEntries < 0)
1146    {
1147      this.maxChangeLogEntries = 0;
1148    }
1149    else
1150    {
1151      this.maxChangeLogEntries = maxChangeLogEntries;
1152    }
1153  }
1154
1155
1156
1157  /**
1158   * Retrieves the maximum number of concurrent connections that the server will
1159   * allow.  If a client tries to establish a new connection while the server
1160   * already has the maximum number of concurrent connections, then the new
1161   * connection will be rejected.  Note that if the server is configured with
1162   * multiple listeners, then each listener will be allowed to have up to this
1163   * number of connections.
1164   *
1165   * @return  The maximum number of concurrent connections that the server will
1166   *          allow, or zero if no limit should be enforced.
1167   */
1168  public int getMaxConnections()
1169  {
1170    return maxConnections;
1171  }
1172
1173
1174
1175  /**
1176   * Specifies the maximum number of concurrent connections that the server will
1177   * allow.  If a client tries to establish a new connection while the server
1178   * already has the maximum number of concurrent connections, then the new
1179   * connection will be rejected.  Note that if the server is configured with
1180   * multiple listeners, then each listener will be allowed to have up to this
1181   * number of connections.
1182   *
1183   * @param  maxConnections  The maximum number of concurrent connections that
1184   *                         the server will allow.  A value that is less than
1185   *                         or equal to zero indicates no limit.
1186   */
1187  public void setMaxConnections(final int maxConnections)
1188  {
1189    if (maxConnections > 0)
1190    {
1191      this.maxConnections = maxConnections;
1192    }
1193    else
1194    {
1195      this.maxConnections = 0;
1196    }
1197  }
1198
1199
1200
1201  /**
1202   * Retrieves the maximum number of entries that the server should return in
1203   * any search operation.
1204   *
1205   * @return  The maximum number of entries that the server should return in any
1206   *          search operation, or zero if no limit should be enforced.
1207   */
1208  public int getMaxSizeLimit()
1209  {
1210    return maxSizeLimit;
1211  }
1212
1213
1214
1215  /**
1216   * Specifies the maximum number of entries that the server should return in
1217   * any search operation.  A value less than or equal to zero indicates that no
1218   * maximum limit should be enforced.
1219   *
1220   * @param  maxSizeLimit  The maximum number of entries that the server should
1221   *                       return in any search operation.
1222   */
1223  public void setMaxSizeLimit(final int maxSizeLimit)
1224  {
1225    if (maxSizeLimit > 0)
1226    {
1227      this.maxSizeLimit = maxSizeLimit;
1228    }
1229    else
1230    {
1231      this.maxSizeLimit = 0;
1232    }
1233  }
1234
1235
1236
1237  /**
1238   * Retrieves a list containing the names or OIDs of the attribute types for
1239   * which to maintain an equality index to improve the performance of certain
1240   * kinds of searches.
1241   *
1242   * @return  A list containing the names or OIDs of the attribute types for
1243   *          which to maintain an equality index to improve the performance of
1244   *          certain kinds of searches, or an empty list if no equality indexes
1245   *          should be created.
1246   */
1247  public List<String> getEqualityIndexAttributes()
1248  {
1249    return equalityIndexAttributes;
1250  }
1251
1252
1253
1254  /**
1255   * Specifies the names or OIDs of the attribute types for which to maintain an
1256   * equality index to improve the performance of certain kinds of searches.
1257   *
1258   * @param  equalityIndexAttributes  The names or OIDs of the attributes for
1259   *                                  which to maintain an equality index to
1260   *                                  improve the performance of certain kinds
1261   *                                  of searches.  It may be {@code null} or
1262   *                                  empty to indicate that no equality indexes
1263   *                                  should be maintained.
1264   */
1265  public void setEqualityIndexAttributes(
1266                   final String... equalityIndexAttributes)
1267  {
1268    setEqualityIndexAttributes(StaticUtils.toList(equalityIndexAttributes));
1269  }
1270
1271
1272
1273  /**
1274   * Specifies the names or OIDs of the attribute types for which to maintain an
1275   * equality index to improve the performance of certain kinds of searches.
1276   *
1277   * @param  equalityIndexAttributes  The names or OIDs of the attributes for
1278   *                                  which to maintain an equality index to
1279   *                                  improve the performance of certain kinds
1280   *                                  of searches.  It may be {@code null} or
1281   *                                  empty to indicate that no equality indexes
1282   *                                  should be maintained.
1283   */
1284  public void setEqualityIndexAttributes(
1285                   final Collection<String> equalityIndexAttributes)
1286  {
1287    this.equalityIndexAttributes.clear();
1288    if (equalityIndexAttributes != null)
1289    {
1290      this.equalityIndexAttributes.addAll(equalityIndexAttributes);
1291    }
1292  }
1293
1294
1295
1296  /**
1297   * Retrieves the names of the attributes for which referential integrity
1298   * should be maintained.  If referential integrity is to be provided and an
1299   * entry is removed, then any other entries containing one of the specified
1300   * attributes with a value equal to the DN of the entry that was removed, then
1301   * that value will also be removed.  Similarly, if an entry is moved or
1302   * renamed, then any references to that entry in one of the specified
1303   * attributes will be updated to reflect the new DN.
1304   *
1305   * @return  The names of the attributes for which referential integrity should
1306   *          be maintained, or an empty set if referential integrity should not
1307   *          be maintained for any attributes.
1308   */
1309  public Set<String> getReferentialIntegrityAttributes()
1310  {
1311    return referentialIntegrityAttributes;
1312  }
1313
1314
1315
1316  /**
1317   * Specifies the names of the attributes for which referential integrity
1318   * should be maintained.  If referential integrity is to be provided and an
1319   * entry is removed, then any other entries containing one of the specified
1320   * attributes with a value equal to the DN of the entry that was removed, then
1321   * that value will also be removed.  Similarly, if an entry is moved or
1322   * renamed, then any references to that entry in one of the specified
1323   * attributes will be updated to reflect the new DN.
1324   *
1325   * @param  referentialIntegrityAttributes  The names of the attributes for
1326   *                                          which referential integrity should
1327   *                                          be maintained.  The values of
1328   *                                          these attributes should be DNs.
1329   *                                          It may be {@code null} or empty if
1330   *                                          referential integrity should not
1331   *                                          be maintained.
1332   */
1333  public void setReferentialIntegrityAttributes(
1334                   final String... referentialIntegrityAttributes)
1335  {
1336    setReferentialIntegrityAttributes(
1337         StaticUtils.toList(referentialIntegrityAttributes));
1338  }
1339
1340
1341
1342  /**
1343   * Specifies the names of the attributes for which referential integrity
1344   * should be maintained.  If referential integrity is to be provided and an
1345   * entry is removed, then any other entries containing one of the specified
1346   * attributes with a value equal to the DN of the entry that was removed, then
1347   * that value will also be removed.  Similarly, if an entry is moved or
1348   * renamed, then any references to that entry in one of the specified
1349   * attributes will be updated to reflect the new DN.
1350   *
1351   * @param  referentialIntegrityAttributes  The names of the attributes for
1352   *                                          which referential integrity should
1353   *                                          be maintained.  The values of
1354   *                                          these attributes should be DNs.
1355   *                                          It may be {@code null} or empty if
1356   *                                          referential integrity should not
1357   *                                          be maintained.
1358   */
1359  public void setReferentialIntegrityAttributes(
1360                   final Collection<String> referentialIntegrityAttributes)
1361  {
1362    this.referentialIntegrityAttributes.clear();
1363    if (referentialIntegrityAttributes != null)
1364    {
1365      this.referentialIntegrityAttributes.addAll(
1366           referentialIntegrityAttributes);
1367    }
1368  }
1369
1370
1371
1372  /**
1373   * Retrieves the vendor name value to report in the server root DSE.
1374   *
1375   * @return  The vendor name value to report in the server root DSE, or
1376   *          {@code null} if no vendor name should appear.
1377   */
1378  public String getVendorName()
1379  {
1380    return vendorName;
1381  }
1382
1383
1384
1385  /**
1386   * Specifies the vendor name value to report in the server root DSE.
1387   *
1388   * @param  vendorName  The vendor name value to report in the server root DSE.
1389   *                     It may be {@code null} if no vendor name should appear.
1390   */
1391  public void setVendorName(final String vendorName)
1392  {
1393    this.vendorName = vendorName;
1394  }
1395
1396
1397
1398  /**
1399   * Retrieves the vendor version value to report in the server root DSE.
1400   *
1401   * @return  The vendor version value to report in the server root DSE, or
1402   *          {@code null} if no vendor version should appear.
1403   */
1404  public String getVendorVersion()
1405  {
1406    return vendorVersion;
1407  }
1408
1409
1410
1411  /**
1412   * Specifies the vendor version value to report in the server root DSE.
1413   *
1414   * @param  vendorVersion  The vendor version value to report in the server
1415   *                        root DSE.  It may be {@code null} if no vendor
1416   *                        version should appear.
1417   */
1418  public void setVendorVersion(final String vendorVersion)
1419  {
1420    this.vendorVersion = vendorVersion;
1421  }
1422
1423
1424
1425  /**
1426   * Retrieves a predefined entry that should always be returned as the
1427   * in-memory directory server's root DSE, if defined.
1428   *
1429   * @return  A predefined entry that should always be returned as the in-memory
1430   *          directory server's root DSE, or {@code null} if the root DSE
1431   *          should be dynamically generated.
1432   */
1433  public ReadOnlyEntry getRootDSEEntry()
1434  {
1435    return rootDSEEntry;
1436  }
1437
1438
1439
1440  /**
1441   * Specifies an entry that should always be returned as the in-memory
1442   * directory server's root DSE.  Note that if a specific root DSE entry is
1443   * provided, then
1444   *
1445   * @param  rootDSEEntry  An entry that should always be returned as the
1446   *                       in-memory directory server's root DSE, or
1447   *                       {@code null} to indicate that the root DSE should be
1448   *                       dynamically generated.
1449   */
1450  public void setRootDSEEntry(final Entry rootDSEEntry)
1451  {
1452    if (rootDSEEntry == null)
1453    {
1454      this.rootDSEEntry = null;
1455      return;
1456    }
1457
1458    final Entry e = rootDSEEntry.duplicate();
1459    e.setDN("");
1460    this.rootDSEEntry = new ReadOnlyEntry(e);
1461  }
1462
1463
1464
1465  /**
1466   * Retrieves an unmodifiable set containing the names or OIDs of the
1467   * attributes that may hold passwords.  These are the attributes whose values
1468   * will be used in bind processing, and clear-text values stored in these
1469   * attributes may be encoded using an {@link InMemoryPasswordEncoder}.
1470   *
1471   * @return  An unmodifiable set containing the names or OIDs of the attributes
1472   *          that may hold passwords, or an empty set if no password attributes
1473   *          have been defined.
1474   */
1475  public Set<String> getPasswordAttributes()
1476  {
1477    return Collections.unmodifiableSet(passwordAttributes);
1478  }
1479
1480
1481
1482  /**
1483   * Specifies the names or OIDs of the attributes that may hold passwords.
1484   * These are the attributes whose values will be used in bind processing, and
1485   * clear-text values stored in these attributes may be encoded using an
1486   * {@link InMemoryPasswordEncoder}.
1487   *
1488   * @param  passwordAttributes  The names or OIDs of the attributes that may
1489   *                             hold passwords.  It may be {@code null} or
1490   *                             empty if there should not be any password
1491   *                             attributes, but that will prevent user
1492   *                             authentication from succeeding.
1493   */
1494  public void setPasswordAttributes(final String... passwordAttributes)
1495  {
1496    setPasswordAttributes(StaticUtils.toList(passwordAttributes));
1497  }
1498
1499
1500
1501  /**
1502   * Specifies the names or OIDs of the attributes that may hold passwords.
1503   * These are the attributes whose values will be used in bind processing, and
1504   * clear-text values stored in these attributes may be encoded using an
1505   * {@link InMemoryPasswordEncoder}.
1506   *
1507   * @param  passwordAttributes  The names or OIDs of the attributes that may
1508   *                             hold passwords.  It may be {@code null} or
1509   *                             empty if there should not be any password
1510   *                             attributes, but that will prevent user
1511   *                             authentication from succeeding.
1512   */
1513  public void setPasswordAttributes(final Collection<String> passwordAttributes)
1514  {
1515    this.passwordAttributes.clear();
1516
1517    if (passwordAttributes != null)
1518    {
1519      this.passwordAttributes.addAll(passwordAttributes);
1520    }
1521  }
1522
1523
1524
1525  /**
1526   * Retrieves the primary password encoder for the in-memory directory server,
1527   * if any.  The primary password encoder will be used to encode the values of
1528   * any clear-text passwords provided in add or modify operations and in LDIF
1529   * imports, and will also be used during authentication processing for any
1530   * encoded passwords that start with the same prefix as this password encoder.
1531   *
1532   * @return  The primary password encoder for the in-memory directory server,
1533   *          or {@code null} if clear-text passwords should be left in the
1534   *          clear without any encoding.
1535   */
1536  public InMemoryPasswordEncoder getPrimaryPasswordEncoder()
1537  {
1538    return primaryPasswordEncoder;
1539  }
1540
1541
1542
1543  /**
1544   * Retrieves an unmodifiable map of the secondary password encoders for the
1545   * in-memory directory server, indexed by prefix.  The secondary password
1546   * encoders will be used to interact with pre-encoded passwords, but will not
1547   * be used to encode new clear-text passwords.
1548   *
1549   * @return  An unmodifiable map of the secondary password encoders for the
1550   *          in-memory directory server, or an empty map if no secondary
1551   *          encoders are defined.
1552   */
1553  public List<InMemoryPasswordEncoder> getSecondaryPasswordEncoders()
1554  {
1555    return Collections.unmodifiableList(secondaryPasswordEncoders);
1556  }
1557
1558
1559
1560  /**
1561   * Specifies the set of password encoders to use for the in-memory directory
1562   * server.  There must not be any conflicts between the prefixes used for any
1563   * of the password encoders (that is, none of the secondary password encoders
1564   * may use the same prefix as the primary password encoder or the same prefix
1565   * as any other secondary password encoder).
1566   * <BR><BR>
1567   * Either or both the primary and secondary encoders may be left undefined.
1568   * If both primary and secondary encoders are left undefined, then the server
1569   * will assume that all passwords are in the clear.  If only a primary encoder
1570   * is configured without any secondary encoders, then the server will encode
1571   * all new passwords that don't start with its prefix.  If only secondary
1572   * encoders are configured without a primary encoder, then all new passwords
1573   * will be left in the clear, but any existing pre-encoded passwords using
1574   * those mechanisms will be handled properly.
1575   *
1576   * @param  primaryEncoder     The primary password encoder to use for the
1577   *                            in-memory directory server.  This encoder will
1578   *                            be used to encode any new clear-text passwords
1579   *                            that are provided to the server in add or modify
1580   *                            operations or in LDIF imports.  It will also be
1581   *                            used to interact with pre-encoded passwords
1582   *                            for any encoded passwords that start with the
1583   *                            same prefix as this password encoder.  It may be
1584   *                            {@code null} if no password encoder is desired
1585   *                            and clear-text passwords should remain in the
1586   *                            clear.
1587   * @param  secondaryEncoders  The secondary password encoders to use when
1588   *                            interacting with pre-encoded passwords, but that
1589   *                            will not be used to encode new clear-text
1590   *                            passwords.  This may be {@code null} or empty if
1591   *                            no secondary password encoders are needed.
1592   *
1593   * @throws  LDAPException  If there is a conflict between the prefixes used by
1594   *                         two or more of the provided encoders.
1595   */
1596  public void setPasswordEncoders(final InMemoryPasswordEncoder primaryEncoder,
1597                   final InMemoryPasswordEncoder... secondaryEncoders)
1598         throws LDAPException
1599  {
1600    setPasswordEncoders(primaryEncoder, StaticUtils.toList(secondaryEncoders));
1601  }
1602
1603
1604
1605  /**
1606   * Specifies the set of password encoders to use for the in-memory directory
1607   * server.  There must not be any conflicts between the prefixes used for any
1608   * of the password encoders (that is, none of the secondary password encoders
1609   * may use the same prefix as the primary password encoder or the same prefix
1610   * as any other secondary password encoder).
1611   * <BR><BR>
1612   * Either or both the primary and secondary encoders may be left undefined.
1613   * If both primary and secondary encoders are left undefined, then the server
1614   * will assume that all passwords are in the clear.  If only a primary encoder
1615   * is configured without any secondary encoders, then the server will encode
1616   * all new passwords that don't start with its prefix.  If only secondary
1617   * encoders are configured without a primary encoder, then all new passwords
1618   * will be left in the clear, but any existing pre-encoded passwords using
1619   * those mechanisms will be handled properly.
1620   *
1621   * @param  primaryEncoder     The primary password encoder to use for the
1622   *                            in-memory directory server.  This encoder will
1623   *                            be used to encode any new clear-text passwords
1624   *                            that are provided to the server in add or modify
1625   *                            operations or in LDIF imports.  It will also be
1626   *                            used to interact with pre-encoded passwords
1627   *                            for any encoded passwords that start with the
1628   *                            same prefix as this password encoder.  It may be
1629   *                            {@code null} if no password encoder is desired
1630   *                            and clear-text passwords should remain in the
1631   *                            clear.
1632   * @param  secondaryEncoders  The secondary password encoders to use when
1633   *                            interacting with pre-encoded passwords, but that
1634   *                            will not be used to encode new clear-text
1635   *                            passwords.  This may be {@code null} or empty if
1636   *                            no secondary password encoders are needed.
1637   *
1638   * @throws  LDAPException  If there is a conflict between the prefixes used by
1639   *                         two or more of the provided encoders.
1640   */
1641  public void setPasswordEncoders(final InMemoryPasswordEncoder primaryEncoder,
1642                   final Collection<InMemoryPasswordEncoder> secondaryEncoders)
1643         throws LDAPException
1644  {
1645    // Before applying the change, make sure that there aren't any conflicts in
1646    // their prefixes.
1647    final LinkedHashMap<String,InMemoryPasswordEncoder> newEncoderMap =
1648         new LinkedHashMap<>(StaticUtils.computeMapCapacity(10));
1649    if (primaryEncoder != null)
1650    {
1651      newEncoderMap.put(primaryEncoder.getPrefix(), primaryEncoder);
1652    }
1653
1654    if (secondaryEncoders != null)
1655    {
1656      for (final InMemoryPasswordEncoder encoder : secondaryEncoders)
1657      {
1658        if (newEncoderMap.containsKey(encoder.getPrefix()))
1659        {
1660          throw new LDAPException(ResultCode.PARAM_ERROR,
1661               ERR_MEM_DS_CFG_PW_ENCODER_CONFLICT.get(encoder.getPrefix()));
1662        }
1663        else
1664        {
1665          newEncoderMap.put(encoder.getPrefix(), encoder);
1666        }
1667      }
1668    }
1669
1670    primaryPasswordEncoder = primaryEncoder;
1671
1672    if (primaryEncoder != null)
1673    {
1674      newEncoderMap.remove(primaryEncoder.getPrefix());
1675    }
1676
1677    secondaryPasswordEncoders.clear();
1678    secondaryPasswordEncoders.addAll(newEncoderMap.values());
1679  }
1680
1681
1682
1683  /**
1684   * Parses the provided set of strings as DNs.
1685   *
1686   * @param  dnStrings  The array of strings to be parsed as DNs.
1687   * @param  schema     The schema to use to generate the normalized
1688   *                    representations of the DNs, if available.
1689   *
1690   * @return  The array of parsed DNs.
1691   *
1692   * @throws  LDAPException  If any of the provided strings cannot be parsed as
1693   *                         DNs.
1694   */
1695  private static DN[] parseDNs(final Schema schema, final String... dnStrings)
1696          throws LDAPException
1697  {
1698    if (dnStrings == null)
1699    {
1700      return null;
1701    }
1702
1703    final DN[] dns = new DN[dnStrings.length];
1704    for (int i=0; i < dns.length; i++)
1705    {
1706      dns[i] = new DN(dnStrings[i], schema);
1707    }
1708    return dns;
1709  }
1710
1711
1712
1713  /**
1714   * Retrieves a string representation of this in-memory directory server
1715   * configuration.
1716   *
1717   * @return  A string representation of this in-memory directory server
1718   *          configuration.
1719   */
1720  @Override()
1721  public String toString()
1722  {
1723    final StringBuilder buffer = new StringBuilder();
1724    toString(buffer);
1725    return buffer.toString();
1726  }
1727
1728
1729
1730  /**
1731   * Appends a string representation of this in-memory directory server
1732   * configuration to the provided buffer.
1733   *
1734   * @param  buffer  The buffer to which the string representation should be
1735   *                 appended.
1736   */
1737  public void toString(final StringBuilder buffer)
1738  {
1739    buffer.append("InMemoryDirectoryServerConfig(baseDNs={");
1740
1741    for (int i=0; i < baseDNs.length; i++)
1742    {
1743      if (i > 0)
1744      {
1745        buffer.append(", ");
1746      }
1747
1748      buffer.append('\'');
1749      baseDNs[i].toString(buffer);
1750      buffer.append('\'');
1751    }
1752    buffer.append('}');
1753
1754    buffer.append(", listenerConfigs={");
1755
1756    final Iterator<InMemoryListenerConfig> listenerCfgIterator =
1757         listenerConfigs.iterator();
1758    while (listenerCfgIterator.hasNext())
1759    {
1760      listenerCfgIterator.next().toString(buffer);
1761      if (listenerCfgIterator.hasNext())
1762      {
1763        buffer.append(", ");
1764      }
1765    }
1766    buffer.append('}');
1767
1768    buffer.append(", schemaProvided=");
1769    buffer.append((schema != null));
1770    buffer.append(", enforceAttributeSyntaxCompliance=");
1771    buffer.append(enforceAttributeSyntaxCompliance);
1772    buffer.append(", enforceSingleStructuralObjectClass=");
1773    buffer.append(enforceSingleStructuralObjectClass);
1774
1775    if (! additionalBindCredentials.isEmpty())
1776    {
1777      buffer.append(", additionalBindDNs={");
1778
1779      final Iterator<DN> bindDNIterator =
1780           additionalBindCredentials.keySet().iterator();
1781      while (bindDNIterator.hasNext())
1782      {
1783        buffer.append('\'');
1784        bindDNIterator.next().toString(buffer);
1785        buffer.append('\'');
1786        if (bindDNIterator.hasNext())
1787        {
1788          buffer.append(", ");
1789        }
1790      }
1791      buffer.append('}');
1792    }
1793
1794    if (! equalityIndexAttributes.isEmpty())
1795    {
1796      buffer.append(", equalityIndexAttributes={");
1797
1798      final Iterator<String> attrIterator = equalityIndexAttributes.iterator();
1799      while (attrIterator.hasNext())
1800      {
1801        buffer.append('\'');
1802        buffer.append(attrIterator.next());
1803        buffer.append('\'');
1804        if (attrIterator.hasNext())
1805        {
1806          buffer.append(", ");
1807        }
1808      }
1809      buffer.append('}');
1810    }
1811
1812    if (! referentialIntegrityAttributes.isEmpty())
1813    {
1814      buffer.append(", referentialIntegrityAttributes={");
1815
1816      final Iterator<String> attrIterator =
1817           referentialIntegrityAttributes.iterator();
1818      while (attrIterator.hasNext())
1819      {
1820        buffer.append('\'');
1821        buffer.append(attrIterator.next());
1822        buffer.append('\'');
1823        if (attrIterator.hasNext())
1824        {
1825          buffer.append(", ");
1826        }
1827      }
1828      buffer.append('}');
1829    }
1830
1831    buffer.append(", generateOperationalAttributes=");
1832    buffer.append(generateOperationalAttributes);
1833
1834    if (maxChangeLogEntries > 0)
1835    {
1836      buffer.append(", maxChangelogEntries=");
1837      buffer.append(maxChangeLogEntries);
1838    }
1839
1840    buffer.append(", maxConnections=");
1841    buffer.append(maxConnections);
1842    buffer.append(", maxSizeLimit=");
1843    buffer.append(maxSizeLimit);
1844
1845    if (! extendedOperationHandlers.isEmpty())
1846    {
1847      buffer.append(", extendedOperationHandlers={");
1848
1849      final Iterator<InMemoryExtendedOperationHandler>
1850           handlerIterator = extendedOperationHandlers.iterator();
1851      while (handlerIterator.hasNext())
1852      {
1853        buffer.append(handlerIterator.next().toString());
1854        if (handlerIterator.hasNext())
1855        {
1856          buffer.append(", ");
1857        }
1858      }
1859      buffer.append('}');
1860    }
1861
1862    if (! saslBindHandlers.isEmpty())
1863    {
1864      buffer.append(", saslBindHandlers={");
1865
1866      final Iterator<InMemorySASLBindHandler>
1867           handlerIterator = saslBindHandlers.iterator();
1868      while (handlerIterator.hasNext())
1869      {
1870        buffer.append(handlerIterator.next().toString());
1871        if (handlerIterator.hasNext())
1872        {
1873          buffer.append(", ");
1874        }
1875      }
1876      buffer.append('}');
1877    }
1878
1879    buffer.append(", passwordAttributes={");
1880    final Iterator<String> pwAttrIterator = passwordAttributes.iterator();
1881    while (pwAttrIterator.hasNext())
1882    {
1883      buffer.append('\'');
1884      buffer.append(pwAttrIterator.next());
1885      buffer.append('\'');
1886
1887      if (pwAttrIterator.hasNext())
1888      {
1889        buffer.append(", ");
1890      }
1891    }
1892    buffer.append('}');
1893
1894    if (primaryPasswordEncoder == null)
1895    {
1896      buffer.append(", primaryPasswordEncoder=null");
1897    }
1898    else
1899    {
1900      buffer.append(", primaryPasswordEncoderPrefix='");
1901      buffer.append(primaryPasswordEncoder.getPrefix());
1902      buffer.append('\'');
1903    }
1904
1905    buffer.append(", secondaryPasswordEncoderPrefixes={");
1906    final Iterator<InMemoryPasswordEncoder> encoderIterator =
1907         secondaryPasswordEncoders.iterator();
1908    while (encoderIterator.hasNext())
1909    {
1910      buffer.append('\'');
1911      buffer.append(encoderIterator.next().getPrefix());
1912      buffer.append('\'');
1913
1914      if (encoderIterator.hasNext())
1915      {
1916        buffer.append(", ");
1917      }
1918    }
1919    buffer.append('}');
1920
1921    if (accessLogHandler != null)
1922    {
1923      buffer.append(", accessLogHandlerClass='");
1924      buffer.append(accessLogHandler.getClass().getName());
1925      buffer.append('\'');
1926    }
1927
1928    if (jsonAccessLogHandler != null)
1929    {
1930      buffer.append(", jsonAccessLogHandlerClass='");
1931      buffer.append(jsonAccessLogHandler.getClass().getName());
1932      buffer.append('\'');
1933    }
1934
1935    if (ldapDebugLogHandler != null)
1936    {
1937      buffer.append(", ldapDebugLogHandlerClass='");
1938      buffer.append(ldapDebugLogHandler.getClass().getName());
1939      buffer.append('\'');
1940    }
1941
1942    if (codeLogPath != null)
1943    {
1944      buffer.append(", codeLogPath='");
1945      buffer.append(codeLogPath);
1946      buffer.append("', includeRequestProcessingInCodeLog=");
1947      buffer.append(includeRequestProcessingInCodeLog);
1948    }
1949
1950    if (exceptionHandler != null)
1951    {
1952      buffer.append(", listenerExceptionHandlerClass='");
1953      buffer.append(exceptionHandler.getClass().getName());
1954      buffer.append('\'');
1955    }
1956
1957    if (vendorName != null)
1958    {
1959      buffer.append(", vendorName='");
1960      buffer.append(vendorName);
1961      buffer.append('\'');
1962    }
1963
1964    if (vendorVersion != null)
1965    {
1966      buffer.append(", vendorVersion='");
1967      buffer.append(vendorVersion);
1968      buffer.append('\'');
1969    }
1970
1971    buffer.append(')');
1972  }
1973}