001/*
002 * Copyright 2020-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2020-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2020-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk;
037
038
039
040import java.util.ArrayList;
041import java.util.Collections;
042import java.util.Iterator;
043import java.util.LinkedHashMap;
044import java.util.List;
045import java.util.Map;
046
047import com.unboundid.asn1.ASN1OctetString;
048import com.unboundid.util.ByteStringBuffer;
049import com.unboundid.util.Debug;
050import com.unboundid.util.NotMutable;
051import com.unboundid.util.NotNull;
052import com.unboundid.util.Nullable;
053import com.unboundid.util.StaticUtils;
054import com.unboundid.util.ThreadSafety;
055import com.unboundid.util.ThreadSafetyLevel;
056import com.unboundid.util.Validator;
057
058
059
060/**
061 * This class provides an implementation of a SASL bind request that uses the
062 * OAUTHBEARER SASL mechanism described in
063 * <A HREF="http://www.ietf.org/rfc/rfc7628.txt">RFC 7628</A> to allow a user
064 * to authenticate with an OAuth 2.0 bearer token.
065 *
066 * @see  OAUTHBEARERBindRequestProperties
067 * @see  OAUTHBEARERBindResult
068 */
069@NotMutable()
070@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
071public final class OAUTHBEARERBindRequest
072       extends SASLBindRequest
073{
074  /**
075   * The name for the OAUTHBEARER SASL mechanism.
076   */
077  @NotNull public static final String OAUTHBEARER_MECHANISM_NAME =
078       "OAUTHBEARER";
079
080
081
082  /**
083   * The delimiter that appears between elements of the GS2 header.
084   */
085  private static final byte GS2_HEADER_DELIMITER = ',';
086
087
088
089  /**
090   * The delimiter that appears after each element of the encoded credentials.
091   */
092  private static final byte OAUTHBEARER_DELIMITER = (byte) 0x01;
093
094
095
096  /**
097   * The component of the GS2 header that indicates that channel binding is not
098   * supported.
099   */
100  @NotNull private static final byte[] GS2_HEADER_ELEMENT_NO_CHANNEL_BINDING =
101       StaticUtils.getBytes("n");
102
103
104
105  /**
106   * The component of the GS2 header that precedes the authorization ID.
107   */
108  @NotNull private static final byte[] GS2_HEADER_ELEMENT_AUTHZ_ID_PREFIX =
109       StaticUtils.getBytes("a=");
110
111
112
113  /**
114   * The component of the OAUTHBEARER bind request credentials that precedes the
115   * access token.
116   */
117  @NotNull private static final byte[]
118       OAUTHBEARER_CRED_ELEMENT_ACCESS_TOKEN_PREFIX =
119            StaticUtils.getBytes("auth=Bearer ");
120
121
122
123  /**
124   * The component of the OAUTHBEARER bind request credentials that precedes the
125   * server address.
126   */
127  @NotNull private static final byte[]
128       OAUTHBEARER_CRED_ELEMENT_SERVER_ADDRESS_PREFIX =
129            StaticUtils.getBytes("host=");
130
131
132
133  /**
134   * The component of the OAUTHBEARER bind request credentials that precedes the
135   * server port.
136   */
137  @NotNull private static final byte[]
138       OAUTHBEARER_CRED_ELEMENT_SERVER_PORT_PREFIX =
139            StaticUtils.getBytes("port=");
140
141
142
143  /**
144   * The component of the OAUTHBEARER bind request credentials that precedes the
145   * request method.
146   */
147  @NotNull private static final byte[]
148       OAUTHBEARER_CRED_ELEMENT_REQUEST_METHOD_PREFIX =
149            StaticUtils.getBytes("mthd=");
150
151
152
153  /**
154   * The component of the OAUTHBEARER bind request credentials that precedes the
155   * request path.
156   */
157  @NotNull private static final byte[]
158       OAUTHBEARER_CRED_ELEMENT_REQUEST_PATH_PREFIX =
159            StaticUtils.getBytes("path=");
160
161
162
163  /**
164   * The component of the OAUTHBEARER bind request credentials that precedes the
165   * request post data.
166   */
167  @NotNull private static final byte[]
168       OAUTHBEARER_CRED_ELEMENT_REQUEST_POST_DATA_PREFIX =
169            StaticUtils.getBytes("post=");
170
171
172
173  /**
174   * The component of the OAUTHBEARER bind request credentials that precedes the
175   * request query string.
176   */
177  @NotNull private static final byte[]
178       OAUTHBEARER_CRED_ELEMENT_REQUEST_QUERY_STRING_PREFIX =
179            StaticUtils.getBytes("qs=");
180
181
182
183  /**
184   * The SASL credentials that should be included in the dummy bind request that
185   * is used to complete a failed authentication attempt.
186   */
187  @NotNull private static final ASN1OctetString DUMMY_REQUEST_CREDENTIALS =
188       new ASN1OctetString(new byte[] { OAUTHBEARER_DELIMITER });
189
190
191
192  /**
193   * The serial version UID for this serializable class.
194   */
195  private static final long serialVersionUID = -1216152242833705618L;
196
197
198
199  // The message ID from the last LDAP message sent from this request.
200  private volatile int messageID;
201
202  // The port of the server to which the request will be sent.
203  @Nullable private final Integer serverPort;
204
205  // A set of additional key-value pairs that should be included in the bind
206  // request.
207  @NotNull private final Map<String,String> additionalKeyValuePairs;
208
209  // The access token to include in the bind request.
210  @NotNull private final String accessToken;
211
212  // The authorization identity to include in the GS2 header for the bind
213  // request.
214  @Nullable private final String authorizationID;
215
216  // The method to use for HTTP-based requests.
217  @Nullable private final String requestMethod;
218
219  // The path to use for HTTP-based requests.
220  @Nullable private final String requestPath;
221
222  // The post data for HTTP-based requests.
223  @Nullable private final String requestPostData;
224
225  // The query string for HTTP-based requests.
226  @Nullable private final String requestQueryString;
227
228  // The address of the server to which the request will be sent.
229  @Nullable private final String serverAddress;
230
231
232
233  /**
234   * Creates a new OAUTHBEARER bind request with the provided access token.
235   * All other properties will be unset.
236   *
237   * @param  accessToken  The access token to use for this bind request.  It
238   *                      must not be {@code null} or empty.
239   * @param  controls     The set of controls to include in the bind request.
240   *                      It may be {@code null} or empty if no controls are
241   *                      needed.
242   */
243  public OAUTHBEARERBindRequest(@NotNull final String accessToken,
244                                @Nullable final Control... controls)
245  {
246    super(controls);
247
248    Validator.ensureNotNullOrEmpty(accessToken,
249         "OAUTHBEARERBindRequest.accessToken must not be null or empty.");
250
251    this.accessToken = accessToken;
252
253    authorizationID = null;
254    serverAddress = null;
255    serverPort = null;
256    requestMethod = null;
257    requestPath = null;
258    requestPostData = null;
259    requestQueryString = null;
260
261    additionalKeyValuePairs = Collections.emptyMap();
262    messageID = -1;
263  }
264
265
266
267  /**
268   * Creates a new OAUTHBEARER bind request with the provided set of properties.
269   *
270   * @param  properties  The set of properties to use to create this bind
271   *                     request.  It must not be {@code null}.
272   * @param  controls    The set of controls to include in the bind request.  It
273   *                     may be {@code null} or empty if no controls are needed.
274   */
275  public OAUTHBEARERBindRequest(
276              @NotNull final OAUTHBEARERBindRequestProperties properties,
277              @Nullable final Control... controls)
278  {
279    super(controls);
280
281    accessToken = properties.getAccessToken();
282    authorizationID = properties.getAuthorizationID();
283    serverAddress = properties.getServerAddress();
284    serverPort = properties.getServerPort();
285    requestMethod = properties.getRequestMethod();
286    requestPath = properties.getRequestPath();
287    requestPostData = properties.getRequestPostData();
288    requestQueryString = properties.getRequestQueryString();
289
290    additionalKeyValuePairs = Collections.unmodifiableMap(
291         new LinkedHashMap<>(properties.getAdditionalKeyValuePairs()));
292
293    messageID = -1;
294  }
295
296
297
298  /**
299   * {@inheritDoc}
300   */
301  @Override()
302  @NotNull()
303  public String getSASLMechanismName()
304  {
305    return OAUTHBEARER_MECHANISM_NAME;
306  }
307
308
309
310  /**
311   * Retrieves the access token to include in the bind request.
312   *
313   * @return  The access token to include in the bind request.
314   */
315  @NotNull()
316  public String getAccessToken()
317  {
318    return accessToken;
319  }
320
321
322
323  /**
324   * Retrieves the authorization ID to include in the GS2 header for the bind
325   * request, if any.
326   *
327   * @return  The authorization ID to include in the GS2 header for the bind
328   *          request, or {@code null} if no authorization ID should be
329   *          included.
330   */
331  @Nullable()
332  public String getAuthorizationID()
333  {
334    return authorizationID;
335  }
336
337
338
339  /**
340   * Retrieves the server address to include in the bind request, if any.
341   *
342   * @return  The server address to include in the bind request, or {@code null}
343   *          if it should be omitted.
344   */
345  @Nullable()
346  public String getServerAddress()
347  {
348    return serverAddress;
349  }
350
351
352
353  /**
354   * Retrieves the server port to include in the bind request, if any.
355   *
356   * @return  The server port to include in the bind request, or {@code null}
357   *          if it should be omitted.
358   */
359  @Nullable()
360  public Integer getServerPort()
361  {
362    return serverPort;
363  }
364
365
366
367  /**
368   * Retrieves the method to use for HTTP-based requests, if any.
369   *
370   * @return  The method to use for HTTP-based requests, or {@code null} if it
371   *          should be omitted from the bind request.
372   */
373  @Nullable()
374  public String getRequestMethod()
375  {
376    return requestMethod;
377  }
378
379
380
381  /**
382   * Retrieves the path to use for HTTP-based requests, if any.
383   *
384   * @return  The path to use for HTTP-based requests, or {@code null} if it
385   *          should be omitted from the bind request.
386   */
387  @Nullable()
388  public String getRequestPath()
389  {
390    return requestPath;
391  }
392
393
394
395  /**
396   * Retrieves the data to submit when posting an HTTP-based request, if any.
397   *
398   * @return  The post data for HTTP-based requests, or {@code null} if it
399   *          should be omitted from the bind request.
400   */
401  @Nullable()
402  public String getRequestPostData()
403  {
404    return requestPostData;
405  }
406
407
408
409  /**
410   * Retrieves the query string to use for HTTP-based requests, if any.
411   *
412   * @return  The query string to use for HTTP-based requests, or {@code null}
413   *          if it should be omitted from the bind request.
414   */
415  @Nullable()
416  public String getRequestQueryString()
417  {
418    return requestQueryString;
419  }
420
421
422
423  /**
424   * Retrieves an unmodifiable map of additional key-value pairs that should be
425   * included in the bind request.
426   *
427   * @return  An unmodifiable map of additional key-value pairs that should be
428   *          included in the bind request.  It will not be {@code null} but may
429   *          be empty.
430   */
431  @NotNull()
432  public Map<String,String> getAdditionalKeyValuePairs()
433  {
434    return additionalKeyValuePairs;
435  }
436
437
438
439  /**
440   * {@inheritDoc}
441   */
442  @Override()
443  @NotNull()
444  protected OAUTHBEARERBindResult process(
445                 @NotNull final LDAPConnection connection, final int depth)
446            throws LDAPException
447  {
448    setReferralDepth(depth);
449
450    // Send the initial request.  If the response has a result code that is
451    // anything other than SASL_BIND_IN_PROGRESS, then we can just return it
452    // directly without needing to do anything else.
453    messageID = InternalSDKHelper.nextMessageID(connection);
454    final BindResult initialBindResult =  sendBindRequest(connection, "",
455         encodeCredentials(), getControls(),
456         getResponseTimeoutMillis(connection));
457    if (initialBindResult.getResultCode() != ResultCode.SASL_BIND_IN_PROGRESS)
458    {
459      return new OAUTHBEARERBindResult(initialBindResult);
460    }
461
462
463    // If we've gotten here, then it indicates that the attempt failed.  We need
464    // to send a second, dummy request to complete the bind process and get the
465    // ultimate failure result.
466    BindResult finalBindResult;
467    try
468    {
469      messageID = InternalSDKHelper.nextMessageID(connection);
470      finalBindResult = sendBindRequest(connection, "",
471           DUMMY_REQUEST_CREDENTIALS, getControls(),
472           getResponseTimeoutMillis(connection));
473    }
474    catch (final LDAPException e)
475    {
476      Debug.debugException(e);
477      finalBindResult = new BindResult(e);
478    }
479
480    return new OAUTHBEARERBindResult(initialBindResult, finalBindResult);
481  }
482
483
484
485  /**
486   * Encodes the credentials as appropriate for this bind request.
487   *
488   * @return  An ASN.1 octet string containing the encoded credentials.
489   */
490  @NotNull()
491  ASN1OctetString encodeCredentials()
492  {
493    final ByteStringBuffer buffer = new ByteStringBuffer();
494
495    // Construct the GS2 header and follow it with the necessary delimiter.
496    buffer.append(GS2_HEADER_ELEMENT_NO_CHANNEL_BINDING);
497    buffer.append(GS2_HEADER_DELIMITER);
498
499    if (authorizationID != null)
500    {
501      buffer.append(GS2_HEADER_ELEMENT_AUTHZ_ID_PREFIX);
502      escapeAuthorizationID(authorizationID, buffer);
503    }
504
505    buffer.append(GS2_HEADER_DELIMITER);
506    buffer.append(OAUTHBEARER_DELIMITER);
507
508
509    // Append the access token.
510    buffer.append(OAUTHBEARER_CRED_ELEMENT_ACCESS_TOKEN_PREFIX);
511    buffer.append(accessToken);
512    buffer.append(OAUTHBEARER_DELIMITER);
513
514
515    // Append the server address, if appropriate.
516    if (serverAddress != null)
517    {
518      buffer.append(OAUTHBEARER_CRED_ELEMENT_SERVER_ADDRESS_PREFIX);
519      buffer.append(serverAddress);
520      buffer.append(OAUTHBEARER_DELIMITER);
521    }
522
523
524    // Append the server port, if appropriate.
525    if (serverPort != null)
526    {
527      buffer.append(OAUTHBEARER_CRED_ELEMENT_SERVER_PORT_PREFIX);
528      buffer.append(serverPort.toString());
529      buffer.append(OAUTHBEARER_DELIMITER);
530    }
531
532
533    // Append the request method, if appropriate.
534    if (requestMethod != null)
535    {
536      buffer.append(OAUTHBEARER_CRED_ELEMENT_REQUEST_METHOD_PREFIX);
537      buffer.append(requestMethod);
538      buffer.append(OAUTHBEARER_DELIMITER);
539    }
540
541
542    // Append the request path, if appropriate.
543    if (requestPath != null)
544    {
545      buffer.append(OAUTHBEARER_CRED_ELEMENT_REQUEST_PATH_PREFIX);
546      buffer.append(requestPath);
547      buffer.append(OAUTHBEARER_DELIMITER);
548    }
549
550
551    // Append the request post data, if appropriate.
552    if (requestPostData != null)
553    {
554      buffer.append(OAUTHBEARER_CRED_ELEMENT_REQUEST_POST_DATA_PREFIX);
555      buffer.append(requestPostData);
556      buffer.append(OAUTHBEARER_DELIMITER);
557    }
558
559
560    // Append the request query string, if appropriate.
561    if (requestQueryString != null)
562    {
563      buffer.append(OAUTHBEARER_CRED_ELEMENT_REQUEST_QUERY_STRING_PREFIX);
564      buffer.append(requestQueryString);
565      buffer.append(OAUTHBEARER_DELIMITER);
566    }
567
568    // Append any additional key-value pairs.
569    for (final Map.Entry<String,String> e : additionalKeyValuePairs.entrySet())
570    {
571      buffer.append(e.getKey());
572      buffer.append('=');
573      buffer.append(e.getValue());
574      buffer.append(OAUTHBEARER_DELIMITER);
575    }
576
577    return new ASN1OctetString(buffer.toByteArray());
578  }
579
580
581
582  /**
583   * Appends an escaped version of the provided authorization ID to the given
584   * buffer.  Any equal signs will be replaced with "=3D" and any commas will be
585   * replaced with "=2C".
586   *
587   * @param  authorizationID  The authorization ID to be escaped.
588   * @param  buffer           The buffer to which the escaped authorization ID
589   *                          should be appended.
590   */
591  private static void escapeAuthorizationID(
592               @NotNull final String authorizationID,
593               @NotNull final ByteStringBuffer buffer)
594  {
595    final int length = authorizationID.length();
596    for (int i=0; i < length; i++)
597    {
598      final char c = authorizationID.charAt(i);
599      switch (c)
600      {
601        case ',':
602          buffer.append("=2C");
603          break;
604        case '=':
605          buffer.append("=3D");
606          break;
607        default:
608          buffer.append(c);
609          break;
610      }
611    }
612  }
613
614
615
616  /**
617   * {@inheritDoc}
618   */
619  @Override()
620  @NotNull()
621  public OAUTHBEARERBindRequest duplicate()
622  {
623    return duplicate(getControls());
624  }
625
626
627
628  /**
629   * {@inheritDoc}
630   */
631  @Override()
632  @NotNull()
633  public OAUTHBEARERBindRequest duplicate(@Nullable final Control[] controls)
634  {
635    final OAUTHBEARERBindRequestProperties properties =
636         new OAUTHBEARERBindRequestProperties(this);
637    final OAUTHBEARERBindRequest bindRequest =
638         new OAUTHBEARERBindRequest(properties, controls);
639    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
640    bindRequest.setIntermediateResponseListener(
641         getIntermediateResponseListener());
642    bindRequest.setReferralDepth(getReferralDepth());
643    bindRequest.setReferralConnector(getReferralConnectorInternal());
644    return bindRequest;
645  }
646
647
648
649  /**
650   * {@inheritDoc}
651   */
652  @Override()
653  public int getLastMessageID()
654  {
655    return messageID;
656  }
657
658
659
660  /**
661   * Retrieves a string representation of the OAUTHBEARER bind request.
662   *
663   * @return  A string representation of the OAUTHBEARER bind request.
664   */
665  @Override()
666  @NotNull()
667  public String toString()
668  {
669    final StringBuilder buffer = new StringBuilder();
670    toString(buffer);
671    return buffer.toString();
672  }
673
674
675
676  /**
677   * Appends a string representation of the OAUTHBEARER bind request to the
678   * provided buffer.
679   *
680   * @param  buffer  The buffer to which the information should be appended.  It
681   *                 must not be {@code null}.
682   */
683  @Override()
684  public void toString(@NotNull final StringBuilder buffer)
685  {
686    buffer.append("OAUTHBEARERBindRequest(accessToken='{redacted}'");
687
688    if (authorizationID != null)
689    {
690      buffer.append(", authorizationID='");
691      buffer.append(authorizationID);
692      buffer.append('\'');
693    }
694
695    if (serverAddress != null)
696    {
697      buffer.append(", serverAddress='");
698      buffer.append(serverAddress);
699      buffer.append('\'');
700    }
701
702    if (serverPort != null)
703    {
704      buffer.append(", serverPort=");
705      buffer.append(serverPort);
706    }
707
708    if (requestMethod != null)
709    {
710      buffer.append(", requestMethod='");
711      buffer.append(requestMethod);
712      buffer.append('\'');
713    }
714
715    if (requestPath != null)
716    {
717      buffer.append(", requestPath='");
718      buffer.append(requestPath);
719      buffer.append('\'');
720    }
721
722    if (requestPostData != null)
723    {
724      buffer.append(", requestPostData='{redacted}'");
725    }
726
727    if (requestQueryString != null)
728    {
729      buffer.append(", requestQueryString='");
730      buffer.append(requestQueryString);
731      buffer.append('\'');
732    }
733
734    if (! additionalKeyValuePairs.isEmpty())
735    {
736      buffer.append(", additionalKeyValuePairs=[");
737
738      final Iterator<Map.Entry<String,String>> iterator =
739           additionalKeyValuePairs.entrySet().iterator();
740      while (iterator.hasNext())
741      {
742        final Map.Entry<String,String> e = iterator.next();
743        buffer.append(" \"");
744        buffer.append(e.getKey());
745        buffer.append("\"=\"");
746        buffer.append(e.getValue());
747        buffer.append('"');
748
749        if (iterator.hasNext())
750        {
751          buffer.append(',');
752        }
753      }
754
755      buffer.append(" ]");
756    }
757
758    buffer.append(')');
759  }
760
761
762
763  /**
764   * {@inheritDoc}
765   */
766  @Override()
767  public void toCode(@NotNull final List<String> lineList,
768                     @NotNull final String requestID,
769                     final int indentSpaces, final boolean includeProcessing)
770  {
771    // Create and update the request properties object.
772    ToCodeHelper.generateMethodCall(lineList, indentSpaces,
773         "OAUTHBEARERBindRequestProperties", requestID + "RequestProperties",
774         "new OAUTHBEARERBindRequestProperties",
775         ToCodeArgHelper.createString(accessToken, "Access Token"));
776
777    if (authorizationID != null)
778    {
779      ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
780           requestID + "RequestProperties.setAuthorizationID",
781           ToCodeArgHelper.createString(authorizationID, null));
782    }
783
784    if (serverAddress != null)
785    {
786      ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
787           requestID + "RequestProperties.setServerAddress",
788           ToCodeArgHelper.createString(serverAddress, null));
789    }
790
791    if (serverPort != null)
792    {
793      ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
794           requestID + "RequestProperties.setServerPort",
795           ToCodeArgHelper.createInteger(serverPort, null));
796    }
797
798    if (requestMethod != null)
799    {
800      ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
801           requestID + "RequestProperties.setRequestMethod",
802           ToCodeArgHelper.createString(requestMethod, null));
803    }
804
805    if (requestPath != null)
806    {
807      ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
808           requestID + "RequestProperties.setRequestPath",
809           ToCodeArgHelper.createString(requestPath, null));
810    }
811
812    if (requestPostData != null)
813    {
814      ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
815           requestID + "RequestProperties.setRequestPostData",
816           ToCodeArgHelper.createString(requestPostData, null));
817    }
818
819    if (requestQueryString != null)
820    {
821      ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
822           requestID + "RequestProperties.setRequestQueryString",
823           ToCodeArgHelper.createString(requestQueryString, null));
824    }
825
826    for (final Map.Entry<String,String> e : additionalKeyValuePairs.entrySet())
827    {
828      ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
829           requestID + "RequestProperties.addKeyValuePair",
830           ToCodeArgHelper.createString(e.getKey(), null),
831           ToCodeArgHelper.createString(e.getValue(), null));
832    }
833
834
835    // Create the request variable.
836    final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(2);
837    constructorArgs.add(
838         ToCodeArgHelper.createRaw(requestID + "RequestProperties", null));
839
840    final Control[] controls = getControls();
841    if (controls.length > 0)
842    {
843      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
844           "Bind Controls"));
845    }
846
847    ToCodeHelper.generateMethodCall(lineList, indentSpaces,
848         "OAUTHBEARERBindRequest", requestID + "Request",
849         "new OAUTHBEARERBindRequest", constructorArgs);
850
851
852    // Add lines for processing the request and obtaining the result.
853    if (includeProcessing)
854    {
855      // Generate a string with the appropriate indent.
856      final StringBuilder buffer = new StringBuilder();
857      for (int i=0; i < indentSpaces; i++)
858      {
859        buffer.append(' ');
860      }
861      final String indent = buffer.toString();
862
863      lineList.add("");
864      lineList.add(indent + "try");
865      lineList.add(indent + '{');
866      lineList.add(indent + "  BindResult " + requestID +
867           "Result = connection.bind(" + requestID + "Request);");
868      lineList.add(indent + "  // The bind was processed successfully.");
869      lineList.add(indent + '}');
870      lineList.add(indent + "catch (LDAPException e)");
871      lineList.add(indent + '{');
872      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
873           "help explain why.");
874      lineList.add(indent + "  // Note that the connection is now likely in " +
875           "an unauthenticated state.");
876      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
877      lineList.add(indent + "  String message = e.getMessage();");
878      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
879      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
880      lineList.add(indent + "  Control[] responseControls = " +
881           "e.getResponseControls();");
882
883      lineList.add("");
884      lineList.add("OAUTHBEARERBindResult bindResult = " +
885                "new OAUTHBEARERBindResult(new BindResult(e));");
886      lineList.add("String authorizationErrorCode = " +
887           "bindResult.getAuthorizationErrorCode();");
888      lineList.add("Set<String> scopes = bindResult.getScopes();");
889      lineList.add("String openIDConfigurationURL = " +
890           "bindResult.getOpenIDConfigurationURL();");
891
892      lineList.add(indent + '}');
893    }
894  }
895}