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