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