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}