001/* 002 * Copyright 2007-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 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 * Creates a new SASL EXTERNAL bind request with the provided set of controls. 124 * 125 * @param controls The set of controls to include in this SASL EXTERNAL 126 * bind request. 127 */ 128 public EXTERNALBindRequest(final Control... controls) 129 { 130 this(null, controls); 131 } 132 133 134 135 /** 136 * Creates a new SASL EXTERNAL bind request with the provided set of controls. 137 * 138 * 139 * @param authzID The authorization ID to use for the bind request. It may 140 * be {@code null} if the client should not send any 141 * authorization ID at all (which may be required by some 142 * servers). It may be an empty string if the server should 143 * determine the authorization identity from what it knows 144 * about the client (e.g., a client certificate). It may be 145 * a non-empty string if the authorization identity should 146 * be different from the authentication identity. 147 * @param controls The set of controls to include in this SASL EXTERNAL 148 * bind request. 149 */ 150 public EXTERNALBindRequest(final String authzID, final Control... controls) 151 { 152 super(controls); 153 154 this.authzID = authzID; 155 } 156 157 158 159 /** 160 * Retrieves the authorization ID that should be included in the bind request, 161 * if any. 162 * 163 * @return The authorization ID that should be included in the bind request, 164 * or {@code null} if the bind request should be sent without an 165 * authorization ID (which is a form that some servers require). It 166 * may be an empty string if the authorization identity should be the 167 * same as the authentication identity and should be determined from 168 * what the server already knows about the client. 169 */ 170 public String getAuthorizationID() 171 { 172 return authzID; 173 } 174 175 176 177 /** 178 * {@inheritDoc} 179 */ 180 @Override() 181 public String getSASLMechanismName() 182 { 183 return EXTERNAL_MECHANISM_NAME; 184 } 185 186 187 188 /** 189 * Sends this bind request to the target server over the provided connection 190 * and returns the corresponding response. 191 * 192 * @param connection The connection to use to send this bind request to the 193 * server and read the associated response. 194 * @param depth The current referral depth for this request. It should 195 * always be one for the initial request, and should only 196 * be incremented when following referrals. 197 * 198 * @return The bind response read from the server. 199 * 200 * @throws LDAPException If a problem occurs while sending the request or 201 * reading the response. 202 */ 203 @Override() 204 protected BindResult process(final LDAPConnection connection, final int depth) 205 throws LDAPException 206 { 207 // Create the LDAP message. 208 messageID = connection.nextMessageID(); 209 210 final ASN1OctetString creds; 211 if (authzID == null) 212 { 213 creds = null; 214 } 215 else 216 { 217 creds = new ASN1OctetString(authzID); 218 } 219 220 return sendBindRequest(connection, "", creds, getControls(), 221 getResponseTimeoutMillis(connection)); 222 } 223 224 225 226 /** 227 * {@inheritDoc} 228 */ 229 @Override() 230 public EXTERNALBindRequest getRebindRequest(final String host, final int port) 231 { 232 return new EXTERNALBindRequest(authzID, getControls()); 233 } 234 235 236 237 /** 238 * {@inheritDoc} 239 */ 240 @Override() 241 public int getLastMessageID() 242 { 243 return messageID; 244 } 245 246 247 248 /** 249 * {@inheritDoc} 250 */ 251 @Override() 252 public EXTERNALBindRequest duplicate() 253 { 254 return duplicate(getControls()); 255 } 256 257 258 259 /** 260 * {@inheritDoc} 261 */ 262 @Override() 263 public EXTERNALBindRequest duplicate(final Control[] controls) 264 { 265 final EXTERNALBindRequest bindRequest = 266 new EXTERNALBindRequest(authzID, controls); 267 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 268 return bindRequest; 269 } 270 271 272 273 /** 274 * {@inheritDoc} 275 */ 276 @Override() 277 public void toString(final StringBuilder buffer) 278 { 279 buffer.append("EXTERNALBindRequest("); 280 281 boolean added = false; 282 if (authzID != null) 283 { 284 buffer.append("authzID='"); 285 buffer.append(authzID); 286 buffer.append('\''); 287 added = true; 288 } 289 290 final Control[] controls = getControls(); 291 if (controls.length > 0) 292 { 293 if (added) 294 { 295 buffer.append(", "); 296 } 297 298 buffer.append("controls={"); 299 for (int i=0; i < controls.length; i++) 300 { 301 if (i > 0) 302 { 303 buffer.append(", "); 304 } 305 306 buffer.append(controls[i]); 307 } 308 buffer.append('}'); 309 } 310 311 buffer.append(')'); 312 } 313 314 315 316 /** 317 * {@inheritDoc} 318 */ 319 @Override() 320 public void toCode(final List<String> lineList, final String requestID, 321 final int indentSpaces, final boolean includeProcessing) 322 { 323 // Create the request variable. 324 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(2); 325 326 if (authzID != null) 327 { 328 constructorArgs.add(ToCodeArgHelper.createString(authzID, 329 "Authorization ID")); 330 } 331 332 final Control[] controls = getControls(); 333 if (controls.length > 0) 334 { 335 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 336 "Bind Controls")); 337 } 338 339 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 340 "EXTERNALBindRequest", requestID + "Request", 341 "new EXTERNALBindRequest", constructorArgs); 342 343 344 // Add lines for processing the request and obtaining the result. 345 if (includeProcessing) 346 { 347 // Generate a string with the appropriate indent. 348 final StringBuilder buffer = new StringBuilder(); 349 for (int i=0; i < indentSpaces; i++) 350 { 351 buffer.append(' '); 352 } 353 final String indent = buffer.toString(); 354 355 lineList.add(""); 356 lineList.add(indent + "try"); 357 lineList.add(indent + '{'); 358 lineList.add(indent + " BindResult " + requestID + 359 "Result = connection.bind(" + requestID + "Request);"); 360 lineList.add(indent + " // The bind was processed successfully."); 361 lineList.add(indent + '}'); 362 lineList.add(indent + "catch (LDAPException e)"); 363 lineList.add(indent + '{'); 364 lineList.add(indent + " // The bind failed. Maybe the following will " + 365 "help explain why."); 366 lineList.add(indent + " // Note that the connection is now likely in " + 367 "an unauthenticated state."); 368 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 369 lineList.add(indent + " String message = e.getMessage();"); 370 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 371 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 372 lineList.add(indent + " Control[] responseControls = " + 373 "e.getResponseControls();"); 374 lineList.add(indent + '}'); 375 } 376 } 377}