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