001    /*
002     * Copyright 2007-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2016 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     */
021    package com.unboundid.ldap.sdk;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.List;
027    
028    import com.unboundid.asn1.ASN1OctetString;
029    import com.unboundid.util.NotMutable;
030    import com.unboundid.util.StaticUtils;
031    import com.unboundid.util.ThreadSafety;
032    import 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)
065    public 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    }