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