001/* 002 * Copyright 2007-2022 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2022 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-2022 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 // Create the LDAP message. 230 messageID = connection.nextMessageID(); 231 232 final ASN1OctetString creds; 233 if (authzID == null) 234 { 235 creds = null; 236 } 237 else 238 { 239 creds = new ASN1OctetString(authzID); 240 } 241 242 return sendBindRequest(connection, "", creds, getControls(), 243 getResponseTimeoutMillis(connection)); 244 } 245 246 247 248 /** 249 * {@inheritDoc} 250 */ 251 @Override() 252 @NotNull() 253 public EXTERNALBindRequest getRebindRequest(@NotNull final String host, 254 final int port) 255 { 256 return new EXTERNALBindRequest(authzID, getControls()); 257 } 258 259 260 261 /** 262 * {@inheritDoc} 263 */ 264 @Override() 265 public int getLastMessageID() 266 { 267 return messageID; 268 } 269 270 271 272 /** 273 * {@inheritDoc} 274 */ 275 @Override() 276 @NotNull() 277 public EXTERNALBindRequest duplicate() 278 { 279 return duplicate(getControls()); 280 } 281 282 283 284 /** 285 * {@inheritDoc} 286 */ 287 @Override() 288 @NotNull() 289 public EXTERNALBindRequest duplicate(@Nullable final Control[] controls) 290 { 291 final EXTERNALBindRequest bindRequest = 292 new EXTERNALBindRequest(authzID, controls); 293 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 294 return bindRequest; 295 } 296 297 298 299 /** 300 * {@inheritDoc} 301 */ 302 @Override() 303 public void toString(@NotNull final StringBuilder buffer) 304 { 305 buffer.append("EXTERNALBindRequest("); 306 307 boolean added = false; 308 if (authzID != null) 309 { 310 buffer.append("authzID='"); 311 buffer.append(authzID); 312 buffer.append('\''); 313 added = true; 314 } 315 316 final Control[] controls = getControls(); 317 if (controls.length > 0) 318 { 319 if (added) 320 { 321 buffer.append(", "); 322 } 323 324 buffer.append("controls={"); 325 for (int i=0; i < controls.length; i++) 326 { 327 if (i > 0) 328 { 329 buffer.append(", "); 330 } 331 332 buffer.append(controls[i]); 333 } 334 buffer.append('}'); 335 } 336 337 buffer.append(')'); 338 } 339 340 341 342 /** 343 * {@inheritDoc} 344 */ 345 @Override() 346 public void toCode(@NotNull final List<String> lineList, 347 @NotNull final String requestID, 348 final int indentSpaces, final boolean includeProcessing) 349 { 350 // Create the request variable. 351 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(2); 352 353 if (authzID != null) 354 { 355 constructorArgs.add(ToCodeArgHelper.createString(authzID, 356 "Authorization ID")); 357 } 358 359 final Control[] controls = getControls(); 360 if (controls.length > 0) 361 { 362 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 363 "Bind Controls")); 364 } 365 366 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 367 "EXTERNALBindRequest", requestID + "Request", 368 "new EXTERNALBindRequest", constructorArgs); 369 370 371 // Add lines for processing the request and obtaining the result. 372 if (includeProcessing) 373 { 374 // Generate a string with the appropriate indent. 375 final StringBuilder buffer = new StringBuilder(); 376 for (int i=0; i < indentSpaces; i++) 377 { 378 buffer.append(' '); 379 } 380 final String indent = buffer.toString(); 381 382 lineList.add(""); 383 lineList.add(indent + "try"); 384 lineList.add(indent + '{'); 385 lineList.add(indent + " BindResult " + requestID + 386 "Result = connection.bind(" + requestID + "Request);"); 387 lineList.add(indent + " // The bind was processed successfully."); 388 lineList.add(indent + '}'); 389 lineList.add(indent + "catch (LDAPException e)"); 390 lineList.add(indent + '{'); 391 lineList.add(indent + " // The bind failed. Maybe the following will " + 392 "help explain why."); 393 lineList.add(indent + " // Note that the connection is now likely in " + 394 "an unauthenticated state."); 395 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 396 lineList.add(indent + " String message = e.getMessage();"); 397 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 398 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 399 lineList.add(indent + " Control[] responseControls = " + 400 "e.getResponseControls();"); 401 lineList.add(indent + '}'); 402 } 403 } 404}