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