001/*
002 * Copyright 2007-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2019 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;
033
034
035
036/**
037 * This class provides a SASL EXTERNAL bind request implementation as described
038 * in <A HREF="http://www.ietf.org/rfc/rfc4422.txt">RFC 4422</A>.  The
039 * EXTERNAL mechanism is used to authenticate using information that is
040 * available outside of the LDAP layer (e.g., a certificate presented by the
041 * client during SSL or StartTLS negotiation).
042 * <BR><BR>
043 * <H2>Example</H2>
044 * The following example demonstrates the process for performing an EXTERNAL
045 * bind against a directory server:
046 * <PRE>
047 * EXTERNALBindRequest bindRequest = new EXTERNALBindRequest("");
048 * BindResult bindResult;
049 * try
050 * {
051 *   bindResult = connection.bind(bindRequest);
052 *   // If we get here, then the bind was successful.
053 * }
054 * catch (LDAPException le)
055 * {
056 *   // The bind failed for some reason.
057 *   bindResult = new BindResult(le.toLDAPResult());
058 *   ResultCode resultCode = le.getResultCode();
059 *   String errorMessageFromServer = le.getDiagnosticMessage();
060 * }
061 * </PRE>
062 */
063@NotMutable()
064@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
065public final class EXTERNALBindRequest
066       extends SASLBindRequest
067{
068  /**
069   * The name for the EXTERNAL SASL mechanism.
070   */
071  public static final String EXTERNAL_MECHANISM_NAME = "EXTERNAL";
072
073
074
075  /**
076   * The serial version UID for this serializable class.
077   */
078  private static final long serialVersionUID = 7520760039662616663L;
079
080
081
082  // The message ID from the last LDAP message sent from this request.
083  private int messageID = -1;
084
085  // The authorization ID to send to the server in the bind request.  It may be
086  // null, empty, or non-empty.
087  private final String authzID;
088
089
090
091  /**
092   * Creates a new SASL EXTERNAL bind request with no authorization ID and no
093   * controls.
094   */
095  public EXTERNALBindRequest()
096  {
097    this(null, StaticUtils.NO_CONTROLS);
098  }
099
100
101
102  /**
103   * Creates a new SASL EXTERNAL bind request with the specified authorization
104   * ID and no controls.
105   *
106   * @param  authzID  The authorization ID to use for the bind request.  It may
107   *                  be {@code null} if the client should not send any
108   *                  authorization ID at all (which may be required by some
109   *                  servers).  It may be an empty string if the server should
110   *                  determine the authorization identity from what it knows
111   *                  about the client (e.g., a client certificate).  It may be
112   *                  a non-empty string if the authorization identity should
113   *                  be different from the authentication identity.
114   */
115  public EXTERNALBindRequest(final String authzID)
116  {
117    this(authzID, StaticUtils.NO_CONTROLS);
118  }
119
120
121
122  /**
123   * Creates a new SASL EXTERNAL bind request with the provided set of controls.
124   *
125   * @param  controls  The set of controls to include in this SASL EXTERNAL
126   *                   bind request.
127   */
128  public EXTERNALBindRequest(final Control... controls)
129  {
130    this(null, controls);
131  }
132
133
134
135  /**
136   * Creates a new SASL EXTERNAL bind request with the provided set of controls.
137   *
138   *
139   * @param  authzID   The authorization ID to use for the bind request.  It may
140   *                   be {@code null} if the client should not send any
141   *                   authorization ID at all (which may be required by some
142   *                   servers).  It may be an empty string if the server should
143   *                   determine the authorization identity from what it knows
144   *                   about the client (e.g., a client certificate).  It may be
145   *                   a non-empty string if the authorization identity should
146   *                   be different from the authentication identity.
147   * @param  controls  The set of controls to include in this SASL EXTERNAL
148   *                   bind request.
149   */
150  public EXTERNALBindRequest(final String authzID, final Control... controls)
151  {
152    super(controls);
153
154    this.authzID = authzID;
155  }
156
157
158
159  /**
160   * Retrieves the authorization ID that should be included in the bind request,
161   * if any.
162   *
163   * @return  The authorization ID that should be included in the bind request,
164   *          or {@code null} if the bind request should be sent without an
165   *          authorization ID (which is a form that some servers require).  It
166   *          may be an empty string if the authorization identity should be the
167   *          same as the authentication identity and should be determined from
168   *          what the server already knows about the client.
169   */
170  public String getAuthorizationID()
171  {
172    return authzID;
173  }
174
175
176
177  /**
178   * {@inheritDoc}
179   */
180  @Override()
181  public String getSASLMechanismName()
182  {
183    return EXTERNAL_MECHANISM_NAME;
184  }
185
186
187
188  /**
189   * Sends this bind request to the target server over the provided connection
190   * and returns the corresponding response.
191   *
192   * @param  connection  The connection to use to send this bind request to the
193   *                     server and read the associated response.
194   * @param  depth       The current referral depth for this request.  It should
195   *                     always be one for the initial request, and should only
196   *                     be incremented when following referrals.
197   *
198   * @return  The bind response read from the server.
199   *
200   * @throws  LDAPException  If a problem occurs while sending the request or
201   *                         reading the response.
202   */
203  @Override()
204  protected BindResult process(final LDAPConnection connection, final int depth)
205            throws LDAPException
206  {
207    // Create the LDAP message.
208    messageID = connection.nextMessageID();
209
210    final ASN1OctetString creds;
211    if (authzID == null)
212    {
213      creds = null;
214    }
215    else
216    {
217      creds = new ASN1OctetString(authzID);
218    }
219
220    return sendBindRequest(connection, "", creds, getControls(),
221                           getResponseTimeoutMillis(connection));
222  }
223
224
225
226  /**
227   * {@inheritDoc}
228   */
229  @Override()
230  public EXTERNALBindRequest getRebindRequest(final String host, final int port)
231  {
232    return new EXTERNALBindRequest(authzID, getControls());
233  }
234
235
236
237  /**
238   * {@inheritDoc}
239   */
240  @Override()
241  public int getLastMessageID()
242  {
243    return messageID;
244  }
245
246
247
248  /**
249   * {@inheritDoc}
250   */
251  @Override()
252  public EXTERNALBindRequest duplicate()
253  {
254    return duplicate(getControls());
255  }
256
257
258
259  /**
260   * {@inheritDoc}
261   */
262  @Override()
263  public EXTERNALBindRequest duplicate(final Control[] controls)
264  {
265    final EXTERNALBindRequest bindRequest =
266         new EXTERNALBindRequest(authzID, controls);
267    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
268    return bindRequest;
269  }
270
271
272
273  /**
274   * {@inheritDoc}
275   */
276  @Override()
277  public void toString(final StringBuilder buffer)
278  {
279    buffer.append("EXTERNALBindRequest(");
280
281    boolean added = false;
282    if (authzID != null)
283    {
284      buffer.append("authzID='");
285      buffer.append(authzID);
286      buffer.append('\'');
287      added = true;
288    }
289
290    final Control[] controls = getControls();
291    if (controls.length > 0)
292    {
293      if (added)
294      {
295        buffer.append(", ");
296      }
297
298      buffer.append("controls={");
299      for (int i=0; i < controls.length; i++)
300      {
301        if (i > 0)
302        {
303          buffer.append(", ");
304        }
305
306        buffer.append(controls[i]);
307      }
308      buffer.append('}');
309    }
310
311    buffer.append(')');
312  }
313
314
315
316  /**
317   * {@inheritDoc}
318   */
319  @Override()
320  public void toCode(final List<String> lineList, final String requestID,
321                     final int indentSpaces, final boolean includeProcessing)
322  {
323    // Create the request variable.
324    final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(2);
325
326    if (authzID != null)
327    {
328      constructorArgs.add(ToCodeArgHelper.createString(authzID,
329           "Authorization ID"));
330    }
331
332    final Control[] controls = getControls();
333    if (controls.length > 0)
334    {
335      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
336           "Bind Controls"));
337    }
338
339    ToCodeHelper.generateMethodCall(lineList, indentSpaces,
340         "EXTERNALBindRequest", requestID + "Request",
341         "new EXTERNALBindRequest", constructorArgs);
342
343
344    // Add lines for processing the request and obtaining the result.
345    if (includeProcessing)
346    {
347      // Generate a string with the appropriate indent.
348      final StringBuilder buffer = new StringBuilder();
349      for (int i=0; i < indentSpaces; i++)
350      {
351        buffer.append(' ');
352      }
353      final String indent = buffer.toString();
354
355      lineList.add("");
356      lineList.add(indent + "try");
357      lineList.add(indent + '{');
358      lineList.add(indent + "  BindResult " + requestID +
359           "Result = connection.bind(" + requestID + "Request);");
360      lineList.add(indent + "  // The bind was processed successfully.");
361      lineList.add(indent + '}');
362      lineList.add(indent + "catch (LDAPException e)");
363      lineList.add(indent + '{');
364      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
365           "help explain why.");
366      lineList.add(indent + "  // Note that the connection is now likely in " +
367           "an unauthenticated state.");
368      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
369      lineList.add(indent + "  String message = e.getMessage();");
370      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
371      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
372      lineList.add(indent + "  Control[] responseControls = " +
373           "e.getResponseControls();");
374      lineList.add(indent + '}');
375    }
376  }
377}