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.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  /**
124   * Creates a new SASL EXTERNAL bind request with the provided set of controls.
125   *
126   * @param  controls  The set of controls to include in this SASL EXTERNAL
127   *                   bind request.
128   */
129  public EXTERNALBindRequest(final Control... controls)
130  {
131    this(null, controls);
132  }
133
134
135
136
137  /**
138   * Creates a new SASL EXTERNAL bind request with the provided set of controls.
139   *
140   *
141   * @param  authzID   The authorization ID to use for the bind request.  It may
142   *                   be {@code null} if the client should not send any
143   *                   authorization ID at all (which may be required by some
144   *                   servers).  It may be an empty string if the server should
145   *                   determine the authorization identity from what it knows
146   *                   about the client (e.g., a client certificate).  It may be
147   *                   a non-empty string if the authorization identity should
148   *                   be different from the authentication identity.
149   * @param  controls  The set of controls to include in this SASL EXTERNAL
150   *                   bind request.
151   */
152  public EXTERNALBindRequest(final String authzID, final Control... controls)
153  {
154    super(controls);
155
156    this.authzID = authzID;
157  }
158
159
160
161  /**
162   * Retrieves the authorization ID that should be included in the bind request,
163   * if any.
164   *
165   * @return  The authorization ID that should be included in the bind request,
166   *          or {@code null} if the bind request should be sent without an
167   *          authorization ID (which is a form that some servers require).  It
168   *          may be an empty string if the authorization identity should be the
169   *          same as the authentication identity and should be determined from
170   *          what the server already knows about the client.
171   */
172  public String getAuthorizationID()
173  {
174    return authzID;
175  }
176
177
178
179  /**
180   * {@inheritDoc}
181   */
182  @Override()
183  public String getSASLMechanismName()
184  {
185    return EXTERNAL_MECHANISM_NAME;
186  }
187
188
189
190  /**
191   * Sends this bind request to the target server over the provided connection
192   * and returns the corresponding response.
193   *
194   * @param  connection  The connection to use to send this bind request to the
195   *                     server and read the associated response.
196   * @param  depth       The current referral depth for this request.  It should
197   *                     always be one for the initial request, and should only
198   *                     be incremented when following referrals.
199   *
200   * @return  The bind response read from the server.
201   *
202   * @throws  LDAPException  If a problem occurs while sending the request or
203   *                         reading the response.
204   */
205  @Override()
206  protected BindResult process(final LDAPConnection connection, final int depth)
207            throws LDAPException
208  {
209    // Create the LDAP message.
210    messageID = connection.nextMessageID();
211
212    final ASN1OctetString creds;
213    if (authzID == null)
214    {
215      creds = null;
216    }
217    else
218    {
219      creds = new ASN1OctetString(authzID);
220    }
221
222    return sendBindRequest(connection, "", creds, getControls(),
223                           getResponseTimeoutMillis(connection));
224  }
225
226
227
228  /**
229   * {@inheritDoc}
230   */
231  @Override()
232  public EXTERNALBindRequest getRebindRequest(final String host, final int port)
233  {
234    return new EXTERNALBindRequest(authzID, getControls());
235  }
236
237
238
239  /**
240   * {@inheritDoc}
241   */
242  @Override()
243  public int getLastMessageID()
244  {
245    return messageID;
246  }
247
248
249
250  /**
251   * {@inheritDoc}
252   */
253  @Override()
254  public EXTERNALBindRequest duplicate()
255  {
256    return duplicate(getControls());
257  }
258
259
260
261  /**
262   * {@inheritDoc}
263   */
264  @Override()
265  public EXTERNALBindRequest duplicate(final Control[] controls)
266  {
267    final EXTERNALBindRequest bindRequest =
268         new EXTERNALBindRequest(authzID, controls);
269    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
270    return bindRequest;
271  }
272
273
274
275  /**
276   * {@inheritDoc}
277   */
278  @Override()
279  public void toString(final StringBuilder buffer)
280  {
281    buffer.append("EXTERNALBindRequest(");
282
283    boolean added = false;
284    if (authzID != null)
285    {
286      buffer.append("authzID='");
287      buffer.append(authzID);
288      buffer.append('\'');
289      added = true;
290    }
291
292    final Control[] controls = getControls();
293    if (controls.length > 0)
294    {
295      if (added)
296      {
297        buffer.append(", ");
298      }
299
300      buffer.append("controls={");
301      for (int i=0; i < controls.length; i++)
302      {
303        if (i > 0)
304        {
305          buffer.append(", ");
306        }
307
308        buffer.append(controls[i]);
309      }
310      buffer.append('}');
311    }
312
313    buffer.append(')');
314  }
315
316
317
318  /**
319   * {@inheritDoc}
320   */
321  @Override()
322  public void toCode(final List<String> lineList, final String requestID,
323                     final int indentSpaces, final boolean includeProcessing)
324  {
325    // Create the request variable.
326    final ArrayList<ToCodeArgHelper> constructorArgs =
327         new ArrayList<ToCodeArgHelper>(2);
328
329    if (authzID != null)
330    {
331      constructorArgs.add(ToCodeArgHelper.createString(authzID,
332           "Authorization ID"));
333    }
334
335    final Control[] controls = getControls();
336    if (controls.length > 0)
337    {
338      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
339           "Bind Controls"));
340    }
341
342    ToCodeHelper.generateMethodCall(lineList, indentSpaces,
343         "EXTERNALBindRequest", requestID + "Request",
344         "new EXTERNALBindRequest", constructorArgs);
345
346
347    // Add lines for processing the request and obtaining the result.
348    if (includeProcessing)
349    {
350      // Generate a string with the appropriate indent.
351      final StringBuilder buffer = new StringBuilder();
352      for (int i=0; i < indentSpaces; i++)
353      {
354        buffer.append(' ');
355      }
356      final String indent = buffer.toString();
357
358      lineList.add("");
359      lineList.add(indent + "try");
360      lineList.add(indent + '{');
361      lineList.add(indent + "  BindResult " + requestID +
362           "Result = connection.bind(" + requestID + "Request);");
363      lineList.add(indent + "  // The bind was processed successfully.");
364      lineList.add(indent + '}');
365      lineList.add(indent + "catch (LDAPException e)");
366      lineList.add(indent + '{');
367      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
368           "help explain why.");
369      lineList.add(indent + "  // Note that the connection is now likely in " +
370           "an unauthenticated state.");
371      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
372      lineList.add(indent + "  String message = e.getMessage();");
373      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
374      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
375      lineList.add(indent + "  Control[] responseControls = " +
376           "e.getResponseControls();");
377      lineList.add(indent + '}');
378    }
379  }
380}