001/*
002 * Copyright 2016-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-2018 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.sdk.unboundidds;
022
023
024
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.Iterator;
028import java.util.LinkedHashMap;
029import java.util.List;
030import java.util.Map;
031
032import com.unboundid.asn1.ASN1Boolean;
033import com.unboundid.asn1.ASN1Element;
034import com.unboundid.asn1.ASN1OctetString;
035import com.unboundid.asn1.ASN1Sequence;
036import com.unboundid.ldap.sdk.BindResult;
037import com.unboundid.ldap.sdk.Control;
038import com.unboundid.ldap.sdk.InternalSDKHelper;
039import com.unboundid.ldap.sdk.LDAPConnection;
040import com.unboundid.ldap.sdk.LDAPException;
041import com.unboundid.ldap.sdk.ResultCode;
042import com.unboundid.ldap.sdk.SASLBindRequest;
043import com.unboundid.ldap.sdk.ToCodeArgHelper;
044import com.unboundid.ldap.sdk.ToCodeHelper;
045import com.unboundid.util.Debug;
046import com.unboundid.util.StaticUtils;
047import com.unboundid.util.ThreadSafety;
048import com.unboundid.util.ThreadSafetyLevel;
049import com.unboundid.util.Validator;
050
051import static com.unboundid.ldap.sdk.unboundidds.UnboundIDDSMessages.*;
052
053
054
055/**
056 * This class provides support for an UnboundID-proprietary SASL mechanism that
057 * may be used to indicate that a user has attempted authentication, whether
058 * successfully or not, through some mechanism that is external to the Directory
059 * Server.  If this mechanism is supported in the server, then attempting to
060 * authenticate with it will not change the identity of the client connection,
061 * but will perform additional processing that would normally be completed
062 * during a more traditional authentication attempt.
063 * <BR>
064 * <BLOCKQUOTE>
065 *   <B>NOTE:</B>  This class, and other classes within the
066 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
067 *   supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661
068 *   server products.  These classes provide support for proprietary
069 *   functionality or for external specifications that are not considered stable
070 *   or mature enough to be guaranteed to work in an interoperable way with
071 *   other types of LDAP servers.
072 * </BLOCKQUOTE>
073 * <BR>
074 * This SASL bind request has a mechanism of
075 * "UNBOUNDID-EXTERNALLY-PROCESSED-AUTHENTICATION" and must
076 * include SASL credentials with the following encoding:
077 * <PRE>
078 *   ExternallyProcessedAuthenticationCredentials ::= SEQUENCE {
079 *        authenticationID                          [0] OCTET STRING,
080 *        externalMechanismName                     [1] OCTET STRING,
081 *        externalAuthenticationWasSuccessful       [2] BOOLEAN,
082 *        externalAuthenticationFailureReason       [3] OCTET STRING OPTIONAL,
083 *        externalAuthenticationWasPasswordBased    [4] BOOLEAN DEFAULT TRUE,
084 *        externalAuthenticationWasSecure           [5] BOOLEAN DEFAULT FALSE,
085 *        endClientIPAddress                        [6] OCTET STRING OPTIONAL,
086 *        additionalAccessLogProperties             [7] SEQUENCE OF SEQUENCE {
087 *             propertyName      OCTET STRING,
088 *             propertyValue     OCTET STRING } OPTIONAL,
089 *        ... }
090 * </PRE>
091 * <BR><BR>
092 * In the event that the external authentication was considered successful, the
093 * server will ensure that the target user's account is in a usable state and,
094 * if not, will return a failure response.  If the external authentication was
095 * successful and the user's account is usable, then the server will make any
096 * appropriate password policy state updates (e.g., clearing previous
097 * authentication failures, updating the user's last login time and IP address,
098 * etc.) and return a success result.
099 * <BR><BR>
100 * In the event that the external authentication was not considered successful,
101 * the server may also make corresponding password policy state updates (e.g.,
102 * incrementing the number of authentication failures and locking the account if
103 * appropriate) before returning a failure result.
104 */
105@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
106public final class UnboundIDExternallyProcessedAuthenticationBindRequest
107       extends SASLBindRequest
108{
109  /**
110   * The name for the UnboundID externally-processed authentication SASL
111   * mechanism.
112   */
113  public static final String
114       UNBOUNDID_EXTERNALLY_PROCESSED_AUTH_MECHANISM_NAME =
115            "UNBOUNDID-EXTERNALLY-PROCESSED-AUTHENTICATION";
116
117
118
119  /**
120   * The BER type for the authenticationID element of the bind request.
121   */
122  private static final byte TYPE_AUTHENTICATION_ID = (byte) 0x80;
123
124
125
126  /**
127   * The BER type for the externalMechanismName element of the bind request.
128   */
129  private static final byte TYPE_EXTERNAL_MECHANISM_NAME = (byte) 0x81;
130
131
132
133  /**
134   * The BER type for the externalAuthenticationWasSuccessful element of the
135   * bind request.
136   */
137  private static final byte TYPE_EXTERNAL_AUTH_WAS_SUCCESSFUL = (byte) 0x82;
138
139
140
141  /**
142   * The BER type for the externalAuthenticationFailureReason element of the
143   * bind request.
144   */
145  private static final byte TYPE_EXTERNAL_AUTH_FAILURE_REASON = (byte) 0x83;
146
147
148
149  /**
150   * The BER type for the externalAuthenticationWasPasswordBased element of the
151   * bind request.
152   */
153  private static final byte TYPE_EXTERNAL_AUTH_WAS_PASSWORD_BASED = (byte) 0x84;
154
155
156
157  /**
158   * The BER type for the externalAuthenticationWasSecure element of the bind
159   * request.
160   */
161  private static final byte TYPE_EXTERNAL_AUTH_WAS_SECURE = (byte) 0x85;
162
163
164
165  /**
166   * The BER type for the endClientIPAddress element of the bind request.
167   */
168  private static final byte TYPE_END_CLIENT_IP_ADDRESS = (byte) 0x86;
169
170
171
172  /**
173   * The BER type for the additionalAccessLogProperties element of the bind
174   * request.
175   */
176  private static final byte TYPE_ADDITIONAL_ACCESS_LOG_PROPERTIES = (byte) 0xA7;
177
178
179
180  /**
181   * The serial version UID for this serializable class.
182   */
183  private static final long serialVersionUID = -4312237491980971019L;
184
185
186
187  // The encoded SASL credentials for this bind request.
188  private volatile ASN1OctetString encodedCredentials;
189
190  // Indicates whether the external authentication processing involved a
191  // password.
192  private final boolean externalAuthWasPasswordBased;
193
194  // Indicates whether the external authentication processing is considered to
195  // have been secure.
196  private final boolean externalAuthWasSecure;
197
198  // Indicates whether the external authentication attempt is considered to have
199  // been successful.
200  private final boolean externalAuthWasSuccessful;
201
202  // The message ID from the last LDAP message sent from this request.
203  private volatile int messageID;
204
205  // A map of additional properties that should be recorded in the server's
206  // access log.
207  private final Map<String,String> additionalAccessLogProperties;
208
209  // The authentication ID that identifies the user for whom the external
210  // authentication processing was performed.
211  private final String authenticationID;
212
213  // The IPv4 or IPv6 address of the end client, if available.
214  private final String endClientIPAddress;
215
216  // The reason that the external authentication attempt was considered a
217  // failure.
218  private final String externalAuthFailureReason;
219
220  // The name of the mechanism used for the external authentication attempt.
221  private final String externalMechanismName;
222
223
224
225  /**
226   * Creates a new UNBOUNDID-EXTERNALLY-PROCESSED-AUTHENTICATION bind request
227   * with the provided information.
228   *
229   * @param  authenticationID               The authentication ID that
230   *                                        identifies the user for whom the
231   *                                        external authentication processing
232   *                                        was performed.  This should be
233   *                                        either "dn:" followed by the DN of
234   *                                        the target user's entry, or "u:"
235   *                                        followed by a username.  This must
236   *                                        not be {@code null}.
237   * @param  externalMechanismName          The name of the mechanism used for
238   *                                        the external authentication attempt.
239   *                                        This must not be {@code null}.
240   * @param  externalAuthWasSuccessful      Indicates whether the external
241   *                                        authentication attempt is considered
242   *                                        to have been successful.
243   * @param  externalAuthFailureReason      The reason that the external
244   *                                        authentication attempt was
245   *                                        considered a failure.  This should
246   *                                        be {@code null} if the external
247   *                                        authentication attempt succeeded,
248   *                                        and may be {@code null} if the
249   *                                        external authentication attempt
250   *                                        failed but no failure reason is
251   *                                        available.
252   * @param  externalAuthWasPasswordBased   Indicates whether the external
253   *                                        authentication processing involved a
254   *                                        password.
255   * @param  externalAuthWasSecure          Indicates whether the external
256   *                                        authentication processing was
257   *                                        considered secure.  A mechanism
258   *                                        should only be considered secure if
259   *                                        all credentials were protected in
260   *                                        all communication.
261   * @param  endClientIPAddress             The IPv4 or IPv6 address of the end
262   *                                        client involved in the external
263   *                                        authentication processing.  This may
264   *                                        be {@code null} if the end client
265   *                                        address is not available.
266   * @param  additionalAccessLogProperties  A map of additional properties that
267   *                                        should be recorded in the server's
268   *                                        access log for the external
269   *                                        authentication attempt.  This may be
270   *                                        {@code null} or empty if no
271   *                                        additional access log properties are
272   *                                        required.
273   * @param  controls                       The set of controls to include in
274   *                                        the request.  It may be {@code null}
275   *                                        or empty if no request controls are
276   *                                        needed.
277   */
278  public UnboundIDExternallyProcessedAuthenticationBindRequest(
279              final String authenticationID, final String externalMechanismName,
280              final boolean externalAuthWasSuccessful,
281              final String externalAuthFailureReason,
282              final boolean externalAuthWasPasswordBased,
283              final boolean externalAuthWasSecure,
284              final String endClientIPAddress,
285              final Map<String,String> additionalAccessLogProperties,
286              final Control... controls)
287  {
288    super(controls);
289
290    Validator.ensureNotNull(authenticationID);
291    Validator.ensureNotNull(externalMechanismName);
292
293    this.authenticationID             = authenticationID;
294    this.externalMechanismName        = externalMechanismName;
295    this.externalAuthWasSuccessful    = externalAuthWasSuccessful;
296    this.externalAuthFailureReason    = externalAuthFailureReason;
297    this.externalAuthWasPasswordBased = externalAuthWasPasswordBased;
298    this.externalAuthWasSecure        = externalAuthWasSecure;
299    this.endClientIPAddress           = endClientIPAddress;
300
301    if (additionalAccessLogProperties == null)
302    {
303      this.additionalAccessLogProperties = Collections.emptyMap();
304    }
305    else
306    {
307      this.additionalAccessLogProperties = Collections.unmodifiableMap(
308           new LinkedHashMap<String,String>(additionalAccessLogProperties));
309    }
310
311    messageID = -1;
312    encodedCredentials = null;
313  }
314
315
316
317  /**
318   * Creates a new UNBOUNDID-EXTERNALLY-PROCESSED-AUTHENTICATION bind request
319   * decoded from the provided information.
320   *
321   * @param  saslCredentials  The encoded SASL credentials to be decoded.  It
322   *                          must not be {@code null}.
323   * @param  controls         The set of controls to include in the request.  It
324   *                          may be {@code null} or empty if no request
325   *                          controls are needed.
326   *
327   * @return  The decoded UNBOUNDID-EXTERNALLY-PROCESSED-AUTHENTICATION bind
328   *          request.
329   *
330   * @throws  LDAPException  If the provided SASL credentials are not valid for
331   *                         am UNBOUNDID-EXTERNALLY-PROCESSED-AUTHENTICATION
332   *                         bind request
333   */
334  public static UnboundIDExternallyProcessedAuthenticationBindRequest
335              decodeSASLCredentials(final ASN1OctetString saslCredentials,
336                                    final Control... controls)
337         throws LDAPException
338  {
339    Validator.ensureNotNull(saslCredentials);
340
341    boolean passwordBased = true;
342    boolean secure = false;
343    Boolean successful = null;
344    String failureReason = null;
345    String ipAddress = null;
346    String mechanism = null;
347    String authID = null;
348
349    final LinkedHashMap<String,String> logProperties =
350         new LinkedHashMap<String,String>(10);
351
352    try
353    {
354      for (final ASN1Element e :
355           ASN1Sequence.decodeAsSequence(saslCredentials.getValue()).elements())
356      {
357        switch (e.getType())
358        {
359          case TYPE_AUTHENTICATION_ID:
360            authID = ASN1OctetString.decodeAsOctetString(e).stringValue();
361            break;
362          case TYPE_EXTERNAL_MECHANISM_NAME:
363            mechanism = ASN1OctetString.decodeAsOctetString(e).stringValue();
364            break;
365          case TYPE_EXTERNAL_AUTH_WAS_SUCCESSFUL:
366            successful = ASN1Boolean.decodeAsBoolean(e).booleanValue();
367            break;
368          case TYPE_EXTERNAL_AUTH_FAILURE_REASON:
369            failureReason =
370                 ASN1OctetString.decodeAsOctetString(e).stringValue();
371            break;
372          case TYPE_EXTERNAL_AUTH_WAS_PASSWORD_BASED:
373            passwordBased = ASN1Boolean.decodeAsBoolean(e).booleanValue();
374            break;
375          case TYPE_EXTERNAL_AUTH_WAS_SECURE:
376            secure = ASN1Boolean.decodeAsBoolean(e).booleanValue();
377            break;
378          case TYPE_END_CLIENT_IP_ADDRESS:
379            ipAddress = ASN1OctetString.decodeAsOctetString(e).stringValue();
380            break;
381          case TYPE_ADDITIONAL_ACCESS_LOG_PROPERTIES:
382            for (final ASN1Element propertiesElement :
383                 ASN1Sequence.decodeAsSequence(e).elements())
384            {
385              final ASN1Element[] logPairElements =
386                   ASN1Sequence.decodeAsSequence(propertiesElement).elements();
387              final String name = ASN1OctetString.decodeAsOctetString(
388                   logPairElements[0]).stringValue();
389              final String value = ASN1OctetString.decodeAsOctetString(
390                   logPairElements[1]).stringValue();
391              logProperties.put(name, value);
392            }
393            break;
394        }
395      }
396    }
397    catch (final Exception e)
398    {
399      Debug.debugException(e);
400      throw new LDAPException(ResultCode.DECODING_ERROR,
401           ERR_EXTERNALLY_PROCESSED_AUTH_CANNOT_DECODE_CREDS.get(
402                UNBOUNDID_EXTERNALLY_PROCESSED_AUTH_MECHANISM_NAME,
403                StaticUtils.getExceptionMessage(e)),
404           e);
405    }
406
407    if (authID == null)
408    {
409      throw new LDAPException(ResultCode.DECODING_ERROR,
410           ERR_EXTERNALLY_PROCESSED_AUTH_NO_AUTH_ID.get(
411                UNBOUNDID_EXTERNALLY_PROCESSED_AUTH_MECHANISM_NAME));
412    }
413
414    if (mechanism == null)
415    {
416      throw new LDAPException(ResultCode.DECODING_ERROR,
417           ERR_EXTERNALLY_PROCESSED_AUTH_NO_MECH.get(
418                UNBOUNDID_EXTERNALLY_PROCESSED_AUTH_MECHANISM_NAME));
419    }
420
421    if (successful == null)
422    {
423      throw new LDAPException(ResultCode.DECODING_ERROR,
424           ERR_EXTERNALLY_PROCESSED_AUTH_NO_WAS_SUCCESSFUL.get(
425                UNBOUNDID_EXTERNALLY_PROCESSED_AUTH_MECHANISM_NAME));
426    }
427
428    final UnboundIDExternallyProcessedAuthenticationBindRequest bindRequest =
429         new UnboundIDExternallyProcessedAuthenticationBindRequest(authID,
430              mechanism, successful, failureReason, passwordBased, secure,
431              ipAddress, logProperties, controls);
432    bindRequest.encodedCredentials = saslCredentials;
433
434    return bindRequest;
435  }
436
437
438
439  /**
440   * Retrieves the authentication ID that identifies the user for whom the
441   * external authentication processing was performed.
442   *
443   * @return  The authentication ID that identifies the user for whom the
444   *          external authentication processing was performed.
445   */
446  public String getAuthenticationID()
447  {
448    return authenticationID;
449  }
450
451
452
453  /**
454   * Retrieves the name of the mechanism used for the external authentication
455   * attempt.
456   *
457   * @return  The name of the mechanism used for the external authentication
458   *          attempt.
459   */
460  public String getExternalMechanismName()
461  {
462    return externalMechanismName;
463  }
464
465
466
467  /**
468   * Indicates whether the external authentication attempt is considered to have
469   * been successful.
470   *
471   * @return  {@code true} if the external authentication attempt was considered
472   *          successful, or {@code false} if not.
473   */
474  public boolean externalAuthenticationWasSuccessful()
475  {
476    return externalAuthWasSuccessful;
477  }
478
479
480
481  /**
482   * Retrieves the reason that the external authentication attempt was
483   * considered a failure, if available.
484   *
485   * @return  The reason that the external authentication attempt was considered
486   *          a failure, or {@code null} if no failure reason is available.
487   */
488  public String getExternalAuthenticationFailureReason()
489  {
490    return externalAuthFailureReason;
491  }
492
493
494
495  /**
496   * Indicates whether the external authentication processing involved a
497   * password.
498   *
499   * @return  {@code true} if the external authentication processing involved a
500   *          password, or {@code false} if not.
501   */
502  public boolean externalAuthenticationWasPasswordBased()
503  {
504    return externalAuthWasPasswordBased;
505  }
506
507
508
509  /**
510   * Indicates whether the external authentication processing is considered to
511   * have been secure.
512   *
513   * @return  {@code true} if the external authentication processing was
514   *          considered secure, or {@code false} if not.
515   */
516  public boolean externalAuthenticationWasSecure()
517  {
518    return externalAuthWasSecure;
519  }
520
521
522
523  /**
524   * Retrieves the IPv4 or IPv6 address of the end client involved in the
525   * external authentication processing, if available.
526   *
527   * @return  The IPv4 or IPv6 address of the end client involved in the
528   *          external authentication processing, or {@code null} if this is not
529   *          available.
530   */
531  public String getEndClientIPAddress()
532  {
533    return endClientIPAddress;
534  }
535
536
537
538  /**
539   * Retrieves a map of additional properties that should be recorded in the
540   * server's access log for the external authentication attempt.
541   *
542   * @return  A map of additional properties that should be recorded in the
543   *          server's access log for the external authentication attempt, or an
544   *          empty map if there are no additional log properties.
545   */
546  public Map<String,String> getAdditionalAccessLogProperties()
547  {
548    return additionalAccessLogProperties;
549  }
550
551
552
553
554  /**
555   * {@inheritDoc}
556   */
557  @Override()
558  public String getSASLMechanismName()
559  {
560    return UNBOUNDID_EXTERNALLY_PROCESSED_AUTH_MECHANISM_NAME;
561  }
562
563
564
565  /**
566   * Retrieves an encoded representation of the SASL credentials for this bind
567   * request.
568   *
569   * @return  An encoded representation of the SASL credentials for this bind
570   *          request.
571   */
572  public ASN1OctetString getEncodedCredentials()
573  {
574    if (encodedCredentials == null)
575    {
576      final ArrayList<ASN1Element> credElements = new ArrayList<ASN1Element>(8);
577
578      credElements.add(new ASN1OctetString(TYPE_AUTHENTICATION_ID,
579           authenticationID));
580      credElements.add(new ASN1OctetString(TYPE_EXTERNAL_MECHANISM_NAME,
581           externalMechanismName));
582      credElements.add(new ASN1Boolean(TYPE_EXTERNAL_AUTH_WAS_SUCCESSFUL,
583           externalAuthWasSuccessful));
584
585      if (externalAuthFailureReason != null)
586      {
587        credElements.add(new ASN1OctetString(TYPE_EXTERNAL_AUTH_FAILURE_REASON,
588             externalAuthFailureReason));
589      }
590
591      if (! externalAuthWasPasswordBased)
592      {
593        credElements.add(new ASN1Boolean(TYPE_EXTERNAL_AUTH_WAS_PASSWORD_BASED,
594             false));
595      }
596
597      if (externalAuthWasSecure)
598      {
599        credElements.add(new ASN1Boolean(TYPE_EXTERNAL_AUTH_WAS_SECURE, true));
600      }
601
602      if (endClientIPAddress != null)
603      {
604        credElements.add(new ASN1OctetString(TYPE_END_CLIENT_IP_ADDRESS,
605             endClientIPAddress));
606      }
607
608      if (! additionalAccessLogProperties.isEmpty())
609      {
610        final ArrayList<ASN1Element> logElements =
611             new ArrayList<ASN1Element>(additionalAccessLogProperties.size());
612        for (final Map.Entry<String,String> e :
613             additionalAccessLogProperties.entrySet())
614        {
615          logElements.add(new ASN1Sequence(
616               new ASN1OctetString(e.getKey()),
617               new ASN1OctetString(e.getValue())));
618        }
619
620        credElements.add(new ASN1Sequence(TYPE_ADDITIONAL_ACCESS_LOG_PROPERTIES,
621             logElements));
622      }
623
624      final ASN1Sequence credSequence = new ASN1Sequence(credElements);
625      encodedCredentials = new ASN1OctetString(credSequence.encode());
626    }
627
628    return encodedCredentials;
629  }
630
631
632
633  /**
634   * {@inheritDoc}
635   */
636  @Override()
637  protected BindResult process(final LDAPConnection connection, final int depth)
638            throws LDAPException
639  {
640    messageID = InternalSDKHelper.nextMessageID(connection);
641    return sendBindRequest(connection, "", getEncodedCredentials(),
642         getControls(), getResponseTimeoutMillis(connection));
643  }
644
645
646
647  /**
648   * {@inheritDoc}
649   */
650  @Override()
651  public int getLastMessageID()
652  {
653    return messageID;
654  }
655
656
657
658  /**
659   * {@inheritDoc}
660   */
661  @Override()
662  public UnboundIDExternallyProcessedAuthenticationBindRequest duplicate()
663  {
664    return duplicate(getControls());
665  }
666
667
668
669  /**
670   * {@inheritDoc}
671   */
672  @Override()
673  public UnboundIDExternallyProcessedAuthenticationBindRequest duplicate(
674              final Control[] controls)
675  {
676    final UnboundIDExternallyProcessedAuthenticationBindRequest bindRequest =
677         new UnboundIDExternallyProcessedAuthenticationBindRequest(
678              authenticationID, externalMechanismName,
679              externalAuthWasSuccessful, externalAuthFailureReason,
680              externalAuthWasPasswordBased, externalAuthWasSecure,
681              endClientIPAddress, additionalAccessLogProperties, controls);
682    bindRequest.encodedCredentials = encodedCredentials;
683
684    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
685    return bindRequest;
686  }
687
688
689
690  /**
691   * {@inheritDoc}
692   */
693  @Override()
694  public UnboundIDExternallyProcessedAuthenticationBindRequest getRebindRequest(
695              final String host, final int port)
696  {
697    return duplicate();
698  }
699
700
701
702  /**
703   * {@inheritDoc}
704   */
705  @Override()
706  public void toString(final StringBuilder buffer)
707  {
708    buffer.append("UnboundIDExternallyProcessedAuthenticationBindRequest(" +
709         "authenticationID='");
710    buffer.append(authenticationID);
711    buffer.append("', externalMechanismName='");
712    buffer.append(externalMechanismName);
713    buffer.append("', externalAuthenticationWasSuccessful=");
714    buffer.append(externalAuthWasSuccessful);
715    buffer.append('\'');
716
717    if (externalAuthFailureReason != null)
718    {
719      buffer.append(", externalAuthenticationFailureReason='");
720      buffer.append(externalAuthFailureReason);
721      buffer.append('\'');
722    }
723
724    buffer.append(", externalAuthenticationWasPasswordBased=");
725    buffer.append(externalAuthWasPasswordBased);
726    buffer.append(", externalAuthenticationWasSecure=");
727    buffer.append(externalAuthWasSecure);
728
729    if (endClientIPAddress != null)
730    {
731      buffer.append(", endClientIPAddress='");
732      buffer.append(endClientIPAddress);
733      buffer.append('\'');
734    }
735
736    if (! additionalAccessLogProperties.isEmpty())
737    {
738      buffer.append(", additionalAccessLogProperties={");
739
740      final Iterator<Map.Entry<String,String>> iterator =
741           additionalAccessLogProperties.entrySet().iterator();
742      while (iterator.hasNext())
743      {
744        final Map.Entry<String,String> e = iterator.next();
745
746        buffer.append('\'');
747        buffer.append(e.getKey());
748        buffer.append("'='");
749        buffer.append(e.getValue());
750        buffer.append('\'');
751
752        if (iterator.hasNext())
753        {
754          buffer.append(", ");
755        }
756      }
757
758      buffer.append('}');
759    }
760
761
762    final Control[] controls = getControls();
763    if (controls.length > 0)
764    {
765      buffer.append(", controls={");
766      for (int i=0; i < controls.length; i++)
767      {
768        if (i > 0)
769        {
770          buffer.append(", ");
771        }
772
773        buffer.append(controls[i]);
774      }
775      buffer.append('}');
776    }
777
778    buffer.append(')');
779  }
780
781
782
783  /**
784   * {@inheritDoc}
785   */
786  @Override()
787  public void toCode(final List<String> lineList, final String requestID,
788                     final int indentSpaces, final boolean includeProcessing)
789  {
790    // Create the map of additional log properties.
791    final ArrayList<ToCodeArgHelper> mapConstructorArgs =
792         new ArrayList<ToCodeArgHelper>(1);
793    mapConstructorArgs.add(ToCodeArgHelper.createInteger(
794         additionalAccessLogProperties.size(), "Initial Capacity"));
795
796    ToCodeHelper.generateMethodCall(lineList, indentSpaces,
797         "LinkedHashMap<String,String>",
798         requestID + "AdditionalAccessLogProperties",
799         "new LinkedHashMap<String,String>",
800         mapConstructorArgs);
801
802
803    // Create the method calls used to populate the map.
804    for (final Map.Entry<String,String> e :
805         additionalAccessLogProperties.entrySet())
806    {
807      final ArrayList<ToCodeArgHelper> putArgs =
808           new ArrayList<ToCodeArgHelper>(2);
809      putArgs.add(ToCodeArgHelper.createString(e.getKey(),
810           "Log Property Key"));
811      putArgs.add(ToCodeArgHelper.createString(e.getValue(),
812           "Log Property Value"));
813
814      ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
815           requestID + "AdditionalAccessLogProperties.put", putArgs);
816    }
817
818
819    // Create the request variable.
820    final ArrayList<ToCodeArgHelper> requestConstructorArgs =
821         new ArrayList<ToCodeArgHelper>(8);
822    requestConstructorArgs.add(ToCodeArgHelper.createString(authenticationID,
823         "Authentication ID"));
824    requestConstructorArgs.add(ToCodeArgHelper.createString(
825         externalMechanismName, "External Mechanism Name"));
826    requestConstructorArgs.add(ToCodeArgHelper.createBoolean(
827         externalAuthWasSuccessful, "External Authentication Was Successful"));
828    requestConstructorArgs.add(ToCodeArgHelper.createString(
829         externalAuthFailureReason, "External Authentication Failure Reason"));
830    requestConstructorArgs.add(ToCodeArgHelper.createBoolean(
831         externalAuthWasPasswordBased,
832         "External Authentication Was Password Based"));
833    requestConstructorArgs.add(ToCodeArgHelper.createBoolean(
834         externalAuthWasSecure, "External Authentication Was Secure"));
835    requestConstructorArgs.add(ToCodeArgHelper.createString(endClientIPAddress,
836         "End Client IP Address"));
837    requestConstructorArgs.add(ToCodeArgHelper.createRaw(
838         requestID + "AdditionalAccessLogProperties",
839         "Additional AccessLogProperties"));
840
841    final Control[] controls = getControls();
842    if (controls.length > 0)
843    {
844      requestConstructorArgs.add(ToCodeArgHelper.createControlArray(controls,
845           "Bind Controls"));
846    }
847
848    lineList.add("");
849    ToCodeHelper.generateMethodCall(lineList, indentSpaces,
850         "UnboundIDExternallyProcessedAuthenticationBindRequest",
851         requestID + "Request",
852         "new UnboundIDExternallyProcessedAuthenticationBindRequest",
853         requestConstructorArgs);
854
855
856    // Add lines for processing the request and obtaining the result.
857    if (includeProcessing)
858    {
859      // Generate a string with the appropriate indent.
860      final StringBuilder buffer = new StringBuilder();
861      for (int i=0; i < indentSpaces; i++)
862      {
863        buffer.append(' ');
864      }
865      final String indent = buffer.toString();
866
867      lineList.add("");
868      lineList.add(indent + "try");
869      lineList.add(indent + '{');
870      lineList.add(indent + "  BindResult " + requestID +
871           "Result = connection.bind(" + requestID + "Request);");
872      lineList.add(indent + "  // The bind was processed successfully.");
873      lineList.add(indent + '}');
874      lineList.add(indent + "catch (LDAPException e)");
875      lineList.add(indent + '{');
876      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
877           "help explain why.");
878      lineList.add(indent + "  // Note that the connection is now likely in " +
879           "an unauthenticated state.");
880      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
881      lineList.add(indent + "  String message = e.getMessage();");
882      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
883      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
884      lineList.add(indent + "  Control[] responseControls = " +
885           "e.getResponseControls();");
886      lineList.add(indent + '}');
887    }
888  }
889}