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 }