001/* 002 * Copyright 2007-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2007-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk; 037 038 039 040import java.util.ArrayList; 041import java.util.List; 042 043import com.unboundid.asn1.ASN1OctetString; 044import com.unboundid.util.NotMutable; 045import com.unboundid.util.NotNull; 046import com.unboundid.util.Nullable; 047import com.unboundid.util.StaticUtils; 048import com.unboundid.util.ThreadSafety; 049import com.unboundid.util.ThreadSafetyLevel; 050 051 052 053/** 054 * This class provides a SASL EXTERNAL bind request implementation as described 055 * in <A HREF="http://www.ietf.org/rfc/rfc4422.txt">RFC 4422</A>. The 056 * EXTERNAL mechanism is used to authenticate using information that is 057 * available outside of the LDAP layer (e.g., a certificate presented by the 058 * client during SSL or StartTLS negotiation). 059 * <BR><BR> 060 * <H2>Example</H2> 061 * The following example demonstrates the process for performing an EXTERNAL 062 * bind against a directory server: 063 * <PRE> 064 * EXTERNALBindRequest bindRequest = new EXTERNALBindRequest(""); 065 * BindResult bindResult; 066 * try 067 * { 068 * bindResult = connection.bind(bindRequest); 069 * // If we get here, then the bind was successful. 070 * } 071 * catch (LDAPException le) 072 * { 073 * // The bind failed for some reason. 074 * bindResult = new BindResult(le.toLDAPResult()); 075 * ResultCode resultCode = le.getResultCode(); 076 * String errorMessageFromServer = le.getDiagnosticMessage(); 077 * } 078 * </PRE> 079 */ 080@NotMutable() 081@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 082public final class EXTERNALBindRequest 083 extends SASLBindRequest 084{ 085 /** 086 * The name for the EXTERNAL SASL mechanism. 087 */ 088 @NotNull public static final String EXTERNAL_MECHANISM_NAME = "EXTERNAL"; 089 090 091 092 /** 093 * The serial version UID for this serializable class. 094 */ 095 private static final long serialVersionUID = 7520760039662616663L; 096 097 098 099 // The message ID from the last LDAP message sent from this request. 100 private int messageID = -1; 101 102 // The authorization ID to send to the server in the bind request. It may be 103 // null, empty, or non-empty. 104 @Nullable private final String authzID; 105 106 107 108 /** 109 * Creates a new SASL EXTERNAL bind request with no authorization ID and no 110 * controls. 111 */ 112 public EXTERNALBindRequest() 113 { 114 this(null, StaticUtils.NO_CONTROLS); 115 } 116 117 118 119 /** 120 * Creates a new SASL EXTERNAL bind request with the specified authorization 121 * ID and no controls. 122 * 123 * @param authzID The authorization ID to use for the bind request. It may 124 * be {@code null} if the client should not send any 125 * authorization ID at all (which may be required by some 126 * servers). It may be an empty string if the server should 127 * determine the authorization identity from what it knows 128 * about the client (e.g., a client certificate). It may be 129 * a non-empty string if the authorization identity should 130 * be different from the authentication identity. 131 */ 132 public EXTERNALBindRequest(@Nullable final String authzID) 133 { 134 this(authzID, StaticUtils.NO_CONTROLS); 135 } 136 137 138 139 /** 140 * Creates a new SASL EXTERNAL bind request with the provided set of controls. 141 * 142 * @param controls The set of controls to include in this SASL EXTERNAL 143 * bind request. 144 */ 145 public EXTERNALBindRequest(@Nullable final Control... controls) 146 { 147 this(null, controls); 148 } 149 150 151 152 /** 153 * Creates a new SASL EXTERNAL bind request with the provided set of controls. 154 * 155 * 156 * @param authzID The authorization ID to use for the bind request. It may 157 * be {@code null} if the client should not send any 158 * authorization ID at all (which may be required by some 159 * servers). It may be an empty string if the server should 160 * determine the authorization identity from what it knows 161 * about the client (e.g., a client certificate). It may be 162 * a non-empty string if the authorization identity should 163 * be different from the authentication identity. 164 * @param controls The set of controls to include in this SASL EXTERNAL 165 * bind request. 166 */ 167 public EXTERNALBindRequest(@Nullable final String authzID, 168 @Nullable final Control... controls) 169 { 170 super(controls); 171 172 this.authzID = authzID; 173 } 174 175 176 177 /** 178 * Retrieves the authorization ID that should be included in the bind request, 179 * if any. 180 * 181 * @return The authorization ID that should be included in the bind request, 182 * or {@code null} if the bind request should be sent without an 183 * authorization ID (which is a form that some servers require). It 184 * may be an empty string if the authorization identity should be the 185 * same as the authentication identity and should be determined from 186 * what the server already knows about the client. 187 */ 188 @Nullable() 189 public String getAuthorizationID() 190 { 191 return authzID; 192 } 193 194 195 196 /** 197 * {@inheritDoc} 198 */ 199 @Override() 200 @NotNull() 201 public String getSASLMechanismName() 202 { 203 return EXTERNAL_MECHANISM_NAME; 204 } 205 206 207 208 /** 209 * Sends this bind request to the target server over the provided connection 210 * and returns the corresponding response. 211 * 212 * @param connection The connection to use to send this bind request to the 213 * server and read the associated response. 214 * @param depth The current referral depth for this request. It should 215 * always be one for the initial request, and should only 216 * be incremented when following referrals. 217 * 218 * @return The bind response read from the server. 219 * 220 * @throws LDAPException If a problem occurs while sending the request or 221 * reading the response. 222 */ 223 @Override() 224 @NotNull() 225 protected BindResult process(@NotNull final LDAPConnection connection, 226 final int depth) 227 throws LDAPException 228 { 229 setReferralDepth(depth); 230 231 // Create the LDAP message. 232 messageID = connection.nextMessageID(); 233 234 final ASN1OctetString creds; 235 if (authzID == null) 236 { 237 creds = null; 238 } 239 else 240 { 241 creds = new ASN1OctetString(authzID); 242 } 243 244 return sendBindRequest(connection, "", creds, getControls(), 245 getResponseTimeoutMillis(connection)); 246 } 247 248 249 250 /** 251 * {@inheritDoc} 252 */ 253 @Override() 254 @NotNull() 255 public EXTERNALBindRequest getRebindRequest(@NotNull final String host, 256 final int port) 257 { 258 return new EXTERNALBindRequest(authzID, getControls()); 259 } 260 261 262 263 /** 264 * {@inheritDoc} 265 */ 266 @Override() 267 public int getLastMessageID() 268 { 269 return messageID; 270 } 271 272 273 274 /** 275 * {@inheritDoc} 276 */ 277 @Override() 278 @NotNull() 279 public EXTERNALBindRequest duplicate() 280 { 281 return duplicate(getControls()); 282 } 283 284 285 286 /** 287 * {@inheritDoc} 288 */ 289 @Override() 290 @NotNull() 291 public EXTERNALBindRequest duplicate(@Nullable final Control[] controls) 292 { 293 final EXTERNALBindRequest bindRequest = 294 new EXTERNALBindRequest(authzID, controls); 295 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 296 bindRequest.setIntermediateResponseListener( 297 getIntermediateResponseListener()); 298 bindRequest.setReferralDepth(getReferralDepth()); 299 bindRequest.setReferralConnector(getReferralConnectorInternal()); 300 return bindRequest; 301 } 302 303 304 305 /** 306 * {@inheritDoc} 307 */ 308 @Override() 309 public void toString(@NotNull final StringBuilder buffer) 310 { 311 buffer.append("EXTERNALBindRequest("); 312 313 boolean added = false; 314 if (authzID != null) 315 { 316 buffer.append("authzID='"); 317 buffer.append(authzID); 318 buffer.append('\''); 319 added = true; 320 } 321 322 final Control[] controls = getControls(); 323 if (controls.length > 0) 324 { 325 if (added) 326 { 327 buffer.append(", "); 328 } 329 330 buffer.append("controls={"); 331 for (int i=0; i < controls.length; i++) 332 { 333 if (i > 0) 334 { 335 buffer.append(", "); 336 } 337 338 buffer.append(controls[i]); 339 } 340 buffer.append('}'); 341 } 342 343 buffer.append(')'); 344 } 345 346 347 348 /** 349 * {@inheritDoc} 350 */ 351 @Override() 352 public void toCode(@NotNull final List<String> lineList, 353 @NotNull final String requestID, 354 final int indentSpaces, final boolean includeProcessing) 355 { 356 // Create the request variable. 357 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(2); 358 359 if (authzID != null) 360 { 361 constructorArgs.add(ToCodeArgHelper.createString(authzID, 362 "Authorization ID")); 363 } 364 365 final Control[] controls = getControls(); 366 if (controls.length > 0) 367 { 368 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 369 "Bind Controls")); 370 } 371 372 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 373 "EXTERNALBindRequest", requestID + "Request", 374 "new EXTERNALBindRequest", constructorArgs); 375 376 377 // Add lines for processing the request and obtaining the result. 378 if (includeProcessing) 379 { 380 // Generate a string with the appropriate indent. 381 final StringBuilder buffer = new StringBuilder(); 382 for (int i=0; i < indentSpaces; i++) 383 { 384 buffer.append(' '); 385 } 386 final String indent = buffer.toString(); 387 388 lineList.add(""); 389 lineList.add(indent + "try"); 390 lineList.add(indent + '{'); 391 lineList.add(indent + " BindResult " + requestID + 392 "Result = connection.bind(" + requestID + "Request);"); 393 lineList.add(indent + " // The bind was processed successfully."); 394 lineList.add(indent + '}'); 395 lineList.add(indent + "catch (LDAPException e)"); 396 lineList.add(indent + '{'); 397 lineList.add(indent + " // The bind failed. Maybe the following will " + 398 "help explain why."); 399 lineList.add(indent + " // Note that the connection is now likely in " + 400 "an unauthenticated state."); 401 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 402 lineList.add(indent + " String message = e.getMessage();"); 403 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 404 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 405 lineList.add(indent + " Control[] responseControls = " + 406 "e.getResponseControls();"); 407 lineList.add(indent + '}'); 408 } 409 } 410}