001/*
002 * Copyright 2007-2022 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-2022 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) 2007-2022 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.List;
042
043import com.unboundid.asn1.ASN1OctetString;
044import com.unboundid.util.NotMutable;
045import com.unboundid.util.NotNull;
046import com.unboundid.util.Nullable;
047import com.unboundid.util.StaticUtils;
048import com.unboundid.util.ThreadSafety;
049import com.unboundid.util.ThreadSafetyLevel;
050import com.unboundid.util.Validator;
051
052
053
054/**
055 * This class provides a SASL PLAIN bind request implementation as described in
056 * <A HREF="http://www.ietf.org/rfc/rfc4616.txt">RFC 4616</A>.  The SASL PLAIN
057 * mechanism allows the client to authenticate with an authentication ID and
058 * password, and optionally allows the client to provide an authorization ID for
059 * use in performing subsequent operations.
060 * <BR><BR>
061 * Elements included in a PLAIN bind request include:
062 * <UL>
063 *   <LI>Authentication ID -- A string which identifies the user that is
064 *       attempting to authenticate.  It should be an "authzId" value as
065 *       described in section 5.2.1.8 of
066 *       <A HREF="http://www.ietf.org/rfc/rfc4513.txt">RFC 4513</A>.  That is,
067 *       it should be either "dn:" followed by the distinguished name of the
068 *       target user, or "u:" followed by the username.  If the "u:" form is
069 *       used, then the mechanism used to resolve the provided username to an
070 *       entry may vary from server to server.</LI>
071 *   <LI>Authorization ID -- An optional string which specifies an alternate
072 *       authorization identity that should be used for subsequent operations
073 *       requested on the connection.  Like the authentication ID, the
074 *       authorization ID should use the "authzId" syntax.</LI>
075 *   <LI>Password -- The clear-text password for the target user.</LI>
076 * </UL>
077 * <H2>Example</H2>
078 * The following example demonstrates the process for performing a PLAIN bind
079 * against a directory server with a username of "test.user" and a password of
080 * "password":
081 * <PRE>
082 * PLAINBindRequest bindRequest =
083 *      new PLAINBindRequest("u:test.user", "password");
084 * BindResult bindResult;
085 * try
086 * {
087 *   bindResult = connection.bind(bindRequest);
088 *   // If we get here, then the bind was successful.
089 * }
090 * catch (LDAPException le)
091 * {
092 *   // The bind failed for some reason.
093 *   bindResult = new BindResult(le.toLDAPResult());
094 *   ResultCode resultCode = le.getResultCode();
095 *   String errorMessageFromServer = le.getDiagnosticMessage();
096 * }
097 * </PRE>
098 */
099@NotMutable()
100@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
101public final class PLAINBindRequest
102       extends SASLBindRequest
103{
104  /**
105   * The name for the PLAIN SASL mechanism.
106   */
107  @NotNull public static final String PLAIN_MECHANISM_NAME = "PLAIN";
108
109
110
111  /**
112   * The serial version UID for this serializable class.
113   */
114  private static final long serialVersionUID = -5186140710317748684L;
115
116
117
118  // The password for this bind request.
119  @NotNull private final ASN1OctetString password;
120
121  // The authentication ID string for this bind request.
122  @NotNull private final String authenticationID;
123
124  // The authorization ID string for this bind request, if available.
125  @Nullable private final String authorizationID;
126
127
128
129  /**
130   * Creates a new SASL PLAIN bind request with the provided authentication ID
131   * and password.  It will not include an authorization ID or set of controls.
132   *
133   * @param  authenticationID  The authentication ID for this bind request.  It
134   *                           must not be {@code null}.
135   * @param  password          The password for this bind request.  It must not
136   *                           be {@code null}.
137   */
138  public PLAINBindRequest(@NotNull final String authenticationID,
139                          @NotNull final String password)
140  {
141    this(authenticationID, null, new ASN1OctetString(password), NO_CONTROLS);
142
143    Validator.ensureNotNull(password);
144  }
145
146
147
148  /**
149   * Creates a new SASL PLAIN bind request with the provided authentication ID
150   * and password.  It will not include an authorization ID or set of controls.
151   *
152   * @param  authenticationID  The authentication ID for this bind request.  It
153   *                           must not be {@code null}.
154   * @param  password          The password for this bind request.  It must not
155   *                           be {@code null}.
156   */
157  public PLAINBindRequest(@NotNull final String authenticationID,
158                          @NotNull final byte[] password)
159  {
160    this(authenticationID, null, new ASN1OctetString(password), NO_CONTROLS);
161
162    Validator.ensureNotNull(password);
163  }
164
165
166
167  /**
168   * Creates a new SASL PLAIN bind request with the provided authentication ID
169   * and password.  It will not include an authorization ID or set of controls.
170   *
171   * @param  authenticationID  The authentication ID for this bind request.  It
172   *                           must not be {@code null}.
173   * @param  password          The password for this bind request.  It must not
174   *                           be {@code null}.
175   */
176  public PLAINBindRequest(@NotNull final String authenticationID,
177                          @NotNull final ASN1OctetString password)
178  {
179    this(authenticationID, null, password, NO_CONTROLS);
180  }
181
182
183
184  /**
185   * Creates a new SASL PLAIN bind request with the provided authentication ID,
186   * authorization ID, and password.  It will not include a set of controls.
187   *
188   * @param  authenticationID  The authentication ID for this bind request.  It
189   *                           must not be {@code null}.
190   * @param  authorizationID   The authorization ID for this bind request, or
191   *                           {@code null} if there is to be no authorization
192   *                           ID.
193   * @param  password          The password for this bind request.  It must not
194   *                           be {@code null}.
195   */
196  public PLAINBindRequest(@NotNull final String authenticationID,
197                          @Nullable final String authorizationID,
198                          @NotNull final String password)
199  {
200    this(authenticationID, authorizationID, new ASN1OctetString(password),
201         NO_CONTROLS);
202
203    Validator.ensureNotNull(password);
204  }
205
206
207
208  /**
209   * Creates a new SASL PLAIN bind request with the provided authentication ID,
210   * authorization ID, and password.  It will not include a set of controls.
211   *
212   * @param  authenticationID  The authentication ID for this bind request.  It
213   *                           must not be {@code null}.
214   * @param  authorizationID   The authorization ID for this bind request, or
215   *                           {@code null} if there is to be no authorization
216   *                           ID.
217   * @param  password          The password for this bind request.  It must not
218   *                           be {@code null}.
219   */
220  public PLAINBindRequest(@NotNull final String authenticationID,
221                          @Nullable final String authorizationID,
222                          @NotNull final byte[] password)
223  {
224    this(authenticationID, authorizationID, new ASN1OctetString(password),
225         NO_CONTROLS);
226
227    Validator.ensureNotNull(password);
228  }
229
230
231
232  /**
233   * Creates a new SASL PLAIN bind request with the provided authentication ID,
234   * authorization ID, and password.  It will not include a set of controls.
235   *
236   * @param  authenticationID  The authentication ID for this bind request.  It
237   *                           must not be {@code null}.
238   * @param  authorizationID   The authorization ID for this bind request, or
239   *                           {@code null} if there is to be no authorization
240   *                           ID.
241   * @param  password          The password for this bind request.  It must not
242   *                           be {@code null}.
243   */
244  public PLAINBindRequest(@NotNull final String authenticationID,
245                          @Nullable final String authorizationID,
246                          @NotNull final ASN1OctetString password)
247  {
248    this(authenticationID, authorizationID, password, NO_CONTROLS);
249  }
250
251
252
253  /**
254   * Creates a new SASL PLAIN bind request with the provided authentication ID,
255   * password, and set of controls.  It will not include an authorization ID.
256   *
257   * @param  authenticationID  The authentication ID for this bind request.  It
258   *                           must not be {@code null}.
259   * @param  password          The password for this bind request.  It must not
260   *                           be {@code null}.
261   * @param  controls          The set of controls to include
262   */
263  public PLAINBindRequest(@NotNull final String authenticationID,
264                          @NotNull final String password,
265                          @Nullable final Control... controls)
266  {
267    this(authenticationID, null, new ASN1OctetString(password), controls);
268
269    Validator.ensureNotNull(password);
270  }
271
272
273
274  /**
275   * Creates a new SASL PLAIN bind request with the provided authentication ID,
276   * password, and set of controls.  It will not include an authorization ID.
277   *
278   * @param  authenticationID  The authentication ID for this bind request.  It
279   *                           must not be {@code null}.
280   * @param  password          The password for this bind request.  It must not
281   *                           be {@code null}.
282   * @param  controls          The set of controls to include
283   */
284  public PLAINBindRequest(@NotNull final String authenticationID,
285                          @NotNull final byte[] password,
286                          @Nullable final Control... controls)
287  {
288    this(authenticationID, null, new ASN1OctetString(password), controls);
289
290    Validator.ensureNotNull(password);
291  }
292
293
294
295  /**
296   * Creates a new SASL PLAIN bind request with the provided authentication ID,
297   * password, and set of controls.  It will not include an authorization ID.
298   *
299   * @param  authenticationID  The authentication ID for this bind request.  It
300   *                           must not be {@code null}.
301   * @param  password          The password for this bind request.  It must not
302   *                           be {@code null}.
303   * @param  controls          The set of controls to include
304   */
305  public PLAINBindRequest(@NotNull final String authenticationID,
306                          @NotNull final ASN1OctetString password,
307                          @Nullable final Control... controls)
308  {
309    this(authenticationID, null, password, controls);
310  }
311
312
313
314  /**
315   * Creates a new SASL PLAIN bind request with the provided information.
316   *
317   * @param  authenticationID  The authentication ID for this bind request.  It
318   *                           must not be {@code null}.
319   * @param  authorizationID   The authorization ID for this bind request, or
320   *                           {@code null} if there is to be no authorization
321   *                           ID.
322   * @param  password          The password for this bind request.  It must not
323   *                           be {@code null}.
324   * @param  controls          The set of controls to include
325   */
326  public PLAINBindRequest(@NotNull final String authenticationID,
327                          @Nullable final String authorizationID,
328                          @NotNull final String password,
329                          @Nullable final Control... controls)
330  {
331    this(authenticationID, authorizationID, new ASN1OctetString(password),
332         controls);
333
334    Validator.ensureNotNull(password);
335  }
336
337
338
339  /**
340   * Creates a new SASL PLAIN bind request with the provided information.
341   *
342   * @param  authenticationID  The authentication ID for this bind request.  It
343   *                           must not be {@code null}.
344   * @param  authorizationID   The authorization ID for this bind request, or
345   *                           {@code null} if there is to be no authorization
346   *                           ID.
347   * @param  password          The password for this bind request.  It must not
348   *                           be {@code null}.
349   * @param  controls          The set of controls to include
350   */
351  public PLAINBindRequest(@NotNull final String authenticationID,
352                          @Nullable final String authorizationID,
353                          @NotNull final byte[] password,
354                          @Nullable final Control... controls)
355  {
356    this(authenticationID, authorizationID, new ASN1OctetString(password),
357         controls);
358
359    Validator.ensureNotNull(password);
360  }
361
362
363
364  /**
365   * Creates a new SASL PLAIN bind request with the provided information.
366   *
367   * @param  authenticationID  The authentication ID for this bind request.  It
368   *                           must not be {@code null}.
369   * @param  authorizationID   The authorization ID for this bind request, or
370   *                           {@code null} if there is to be no authorization
371   *                           ID.
372   * @param  password          The password for this bind request.  It must not
373   *                           be {@code null}.
374   * @param  controls          The set of controls to include
375   */
376  public PLAINBindRequest(@NotNull final String authenticationID,
377                          @Nullable final String authorizationID,
378                          @NotNull final ASN1OctetString password,
379                          @Nullable final Control... controls)
380  {
381    super(controls);
382
383    Validator.ensureNotNull(authenticationID, password);
384
385    this.authenticationID = authenticationID;
386    this.authorizationID  = authorizationID;
387    this.password         = password;
388  }
389
390
391
392  /**
393   * {@inheritDoc}
394   */
395  @Override()
396  @NotNull()
397  public String getSASLMechanismName()
398  {
399    return PLAIN_MECHANISM_NAME;
400  }
401
402
403
404  /**
405   * Retrieves the authentication ID for this bind request.
406   *
407   * @return  The authentication ID for this bind request.
408   */
409  @NotNull()
410  public String getAuthenticationID()
411  {
412    return authenticationID;
413  }
414
415
416
417  /**
418   * Retrieves the authorization ID for this bind request.
419   *
420   * @return  The authorization ID for this bind request, or {@code null} if
421   *          there is no authorization ID.
422   */
423  @Nullable()
424  public String getAuthorizationID()
425  {
426    return authorizationID;
427  }
428
429
430
431  /**
432   * Retrieves the string representation of the password for this bind request.
433   *
434   * @return  The string representation of the password for this bind request.
435   */
436  @NotNull()
437  public String getPasswordString()
438  {
439    return password.stringValue();
440  }
441
442
443
444  /**
445   * Retrieves the bytes that comprise the the password for this bind request.
446   *
447   * @return  The bytes that comprise the password for this bind request.
448   */
449  @NotNull()
450  public byte[] getPasswordBytes()
451  {
452    return password.getValue();
453  }
454
455
456
457  /**
458   * Sends this bind request to the target server over the provided connection
459   * and returns the corresponding response.
460   *
461   * @param  connection  The connection to use to send this bind request to the
462   *                     server and read the associated response.
463   * @param  depth       The current referral depth for this request.  It should
464   *                     always be one for the initial request, and should only
465   *                     be incremented when following referrals.
466   *
467   * @return  The bind response read from the server.
468   *
469   * @throws  LDAPException  If a problem occurs while sending the request or
470   *                         reading the response.
471   */
472  @Override()
473  @NotNull()
474  protected BindResult process(@NotNull final LDAPConnection connection,
475                               final int depth)
476            throws LDAPException
477  {
478    // Create the byte array that should comprise the credentials.
479    final byte[] authZIDBytes  = StaticUtils.getBytes(authorizationID);
480    final byte[] authNIDBytes  = StaticUtils.getBytes(authenticationID);
481    final byte[] passwordBytes = password.getValue();
482    final byte[] credBytes     = new byte[2 + authZIDBytes.length +
483                                    authNIDBytes.length + passwordBytes.length];
484
485    System.arraycopy(authZIDBytes, 0, credBytes, 0, authZIDBytes.length);
486
487    int pos = authZIDBytes.length + 1;
488    System.arraycopy(authNIDBytes, 0, credBytes, pos, authNIDBytes.length);
489
490    pos += authNIDBytes.length + 1;
491    System.arraycopy(passwordBytes, 0, credBytes, pos, passwordBytes.length);
492
493    return sendBindRequest(connection, "", new ASN1OctetString(credBytes),
494         getControls(), getResponseTimeoutMillis(connection));
495  }
496
497
498
499  /**
500   * {@inheritDoc}
501   */
502  @Override()
503  @NotNull()
504  public PLAINBindRequest getRebindRequest(@NotNull final String host,
505                                           final int port)
506  {
507    return new PLAINBindRequest(authenticationID, authorizationID, password,
508                                getControls());
509  }
510
511
512
513  /**
514   * {@inheritDoc}
515   */
516  @Override()
517  @NotNull()
518  public PLAINBindRequest duplicate()
519  {
520    return duplicate(getControls());
521  }
522
523
524
525  /**
526   * {@inheritDoc}
527   */
528  @Override()
529  @NotNull()
530  public PLAINBindRequest duplicate(@Nullable final Control[] controls)
531  {
532    final PLAINBindRequest bindRequest = new PLAINBindRequest(authenticationID,
533         authorizationID, password, controls);
534    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
535    return bindRequest;
536  }
537
538
539
540  /**
541   * {@inheritDoc}
542   */
543  @Override()
544  public void toString(@NotNull final StringBuilder buffer)
545  {
546    buffer.append("PLAINBindRequest(authenticationID='");
547    buffer.append(authenticationID);
548    buffer.append('\'');
549
550    if (authorizationID != null)
551    {
552      buffer.append(", authorizationID='");
553      buffer.append(authorizationID);
554      buffer.append('\'');
555    }
556
557    final Control[] controls = getControls();
558    if (controls.length > 0)
559    {
560      buffer.append(", controls={");
561      for (int i=0; i < controls.length; i++)
562      {
563        if (i > 0)
564        {
565          buffer.append(", ");
566        }
567
568        buffer.append(controls[i]);
569      }
570      buffer.append('}');
571    }
572
573    buffer.append(')');
574  }
575
576
577
578  /**
579   * {@inheritDoc}
580   */
581  @Override()
582  public void toCode(@NotNull final List<String> lineList,
583                     @NotNull final String requestID,
584                     final int indentSpaces, final boolean includeProcessing)
585  {
586    // Create the request variable.
587    final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(4);
588    constructorArgs.add(ToCodeArgHelper.createString(authenticationID,
589         "Authentication ID"));
590    constructorArgs.add(ToCodeArgHelper.createString(authorizationID,
591         "Authorization ID"));
592    constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---",
593         "Bind Password"));
594
595    final Control[] controls = getControls();
596    if (controls.length > 0)
597    {
598      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
599           "Bind Controls"));
600    }
601
602    ToCodeHelper.generateMethodCall(lineList, indentSpaces, "PLAINBindRequest",
603         requestID + "Request", "new PLAINBindRequest", constructorArgs);
604
605
606    // Add lines for processing the request and obtaining the result.
607    if (includeProcessing)
608    {
609      // Generate a string with the appropriate indent.
610      final StringBuilder buffer = new StringBuilder();
611      for (int i=0; i < indentSpaces; i++)
612      {
613        buffer.append(' ');
614      }
615      final String indent = buffer.toString();
616
617      lineList.add("");
618      lineList.add(indent + "try");
619      lineList.add(indent + '{');
620      lineList.add(indent + "  BindResult " + requestID +
621           "Result = connection.bind(" + requestID + "Request);");
622      lineList.add(indent + "  // The bind was processed successfully.");
623      lineList.add(indent + '}');
624      lineList.add(indent + "catch (LDAPException e)");
625      lineList.add(indent + '{');
626      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
627           "help explain why.");
628      lineList.add(indent + "  // Note that the connection is now likely in " +
629           "an unauthenticated state.");
630      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
631      lineList.add(indent + "  String message = e.getMessage();");
632      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
633      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
634      lineList.add(indent + "  Control[] responseControls = " +
635           "e.getResponseControls();");
636      lineList.add(indent + '}');
637    }
638  }
639}