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