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