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