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; 050import com.unboundid.util.Validator; 051 052 053 054/** 055 * This class provides a SASL PLAIN bind request implementation as described in 056 * <A HREF="http://www.ietf.org/rfc/rfc4616.txt">RFC 4616</A>. The SASL PLAIN 057 * mechanism allows the client to authenticate with an authentication ID and 058 * password, and optionally allows the client to provide an authorization ID for 059 * use in performing subsequent operations. 060 * <BR><BR> 061 * Elements included in a PLAIN bind request include: 062 * <UL> 063 * <LI>Authentication ID -- A string which identifies the user that is 064 * attempting to authenticate. It should be an "authzId" value as 065 * described in section 5.2.1.8 of 066 * <A HREF="http://www.ietf.org/rfc/rfc4513.txt">RFC 4513</A>. That is, 067 * it should be either "dn:" followed by the distinguished name of the 068 * target user, or "u:" followed by the username. If the "u:" form is 069 * used, then the mechanism used to resolve the provided username to an 070 * entry may vary from server to server.</LI> 071 * <LI>Authorization ID -- An optional string which specifies an alternate 072 * authorization identity that should be used for subsequent operations 073 * requested on the connection. Like the authentication ID, the 074 * authorization ID should use the "authzId" syntax.</LI> 075 * <LI>Password -- The clear-text password for the target user.</LI> 076 * </UL> 077 * <H2>Example</H2> 078 * The following example demonstrates the process for performing a PLAIN bind 079 * against a directory server with a username of "test.user" and a password of 080 * "password": 081 * <PRE> 082 * PLAINBindRequest bindRequest = 083 * new PLAINBindRequest("u:test.user", "password"); 084 * BindResult bindResult; 085 * try 086 * { 087 * bindResult = connection.bind(bindRequest); 088 * // If we get here, then the bind was successful. 089 * } 090 * catch (LDAPException le) 091 * { 092 * // The bind failed for some reason. 093 * bindResult = new BindResult(le.toLDAPResult()); 094 * ResultCode resultCode = le.getResultCode(); 095 * String errorMessageFromServer = le.getDiagnosticMessage(); 096 * } 097 * </PRE> 098 */ 099@NotMutable() 100@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 101public final class PLAINBindRequest 102 extends SASLBindRequest 103{ 104 /** 105 * The name for the PLAIN SASL mechanism. 106 */ 107 @NotNull public static final String PLAIN_MECHANISM_NAME = "PLAIN"; 108 109 110 111 /** 112 * The serial version UID for this serializable class. 113 */ 114 private static final long serialVersionUID = -5186140710317748684L; 115 116 117 118 // The password for this bind request. 119 @NotNull private final ASN1OctetString password; 120 121 // The authentication ID string for this bind request. 122 @NotNull private final String authenticationID; 123 124 // The authorization ID string for this bind request, if available. 125 @Nullable private final String authorizationID; 126 127 128 129 /** 130 * Creates a new SASL PLAIN bind request with the provided authentication ID 131 * and password. It will not include an authorization ID or set of controls. 132 * 133 * @param authenticationID The authentication ID for this bind request. It 134 * must not be {@code null}. 135 * @param password The password for this bind request. It must not 136 * be {@code null}. 137 */ 138 public PLAINBindRequest(@NotNull final String authenticationID, 139 @NotNull final String password) 140 { 141 this(authenticationID, null, new ASN1OctetString(password), NO_CONTROLS); 142 143 Validator.ensureNotNull(password); 144 } 145 146 147 148 /** 149 * Creates a new SASL PLAIN bind request with the provided authentication ID 150 * and password. It will not include an authorization ID or set of controls. 151 * 152 * @param authenticationID The authentication ID for this bind request. It 153 * must not be {@code null}. 154 * @param password The password for this bind request. It must not 155 * be {@code null}. 156 */ 157 public PLAINBindRequest(@NotNull final String authenticationID, 158 @NotNull final byte[] password) 159 { 160 this(authenticationID, null, new ASN1OctetString(password), NO_CONTROLS); 161 162 Validator.ensureNotNull(password); 163 } 164 165 166 167 /** 168 * Creates a new SASL PLAIN bind request with the provided authentication ID 169 * and password. It will not include an authorization ID or set of controls. 170 * 171 * @param authenticationID The authentication ID for this bind request. It 172 * must not be {@code null}. 173 * @param password The password for this bind request. It must not 174 * be {@code null}. 175 */ 176 public PLAINBindRequest(@NotNull final String authenticationID, 177 @NotNull final ASN1OctetString password) 178 { 179 this(authenticationID, null, password, NO_CONTROLS); 180 } 181 182 183 184 /** 185 * Creates a new SASL PLAIN bind request with the provided authentication ID, 186 * authorization ID, and password. It will not include a set of controls. 187 * 188 * @param authenticationID The authentication ID for this bind request. It 189 * must not be {@code null}. 190 * @param authorizationID The authorization ID for this bind request, or 191 * {@code null} if there is to be no authorization 192 * ID. 193 * @param password The password for this bind request. It must not 194 * be {@code null}. 195 */ 196 public PLAINBindRequest(@NotNull final String authenticationID, 197 @Nullable final String authorizationID, 198 @NotNull final String password) 199 { 200 this(authenticationID, authorizationID, new ASN1OctetString(password), 201 NO_CONTROLS); 202 203 Validator.ensureNotNull(password); 204 } 205 206 207 208 /** 209 * Creates a new SASL PLAIN bind request with the provided authentication ID, 210 * authorization ID, and password. It will not include a set of controls. 211 * 212 * @param authenticationID The authentication ID for this bind request. It 213 * must not be {@code null}. 214 * @param authorizationID The authorization ID for this bind request, or 215 * {@code null} if there is to be no authorization 216 * ID. 217 * @param password The password for this bind request. It must not 218 * be {@code null}. 219 */ 220 public PLAINBindRequest(@NotNull final String authenticationID, 221 @Nullable final String authorizationID, 222 @NotNull final byte[] password) 223 { 224 this(authenticationID, authorizationID, new ASN1OctetString(password), 225 NO_CONTROLS); 226 227 Validator.ensureNotNull(password); 228 } 229 230 231 232 /** 233 * Creates a new SASL PLAIN bind request with the provided authentication ID, 234 * authorization ID, and password. It will not include a set of controls. 235 * 236 * @param authenticationID The authentication ID for this bind request. It 237 * must not be {@code null}. 238 * @param authorizationID The authorization ID for this bind request, or 239 * {@code null} if there is to be no authorization 240 * ID. 241 * @param password The password for this bind request. It must not 242 * be {@code null}. 243 */ 244 public PLAINBindRequest(@NotNull final String authenticationID, 245 @Nullable final String authorizationID, 246 @NotNull final ASN1OctetString password) 247 { 248 this(authenticationID, authorizationID, password, NO_CONTROLS); 249 } 250 251 252 253 /** 254 * Creates a new SASL PLAIN bind request with the provided authentication ID, 255 * password, and set of controls. It will not include an authorization ID. 256 * 257 * @param authenticationID The authentication ID for this bind request. It 258 * must not be {@code null}. 259 * @param password The password for this bind request. It must not 260 * be {@code null}. 261 * @param controls The set of controls to include 262 */ 263 public PLAINBindRequest(@NotNull final String authenticationID, 264 @NotNull final String password, 265 @Nullable final Control... controls) 266 { 267 this(authenticationID, null, new ASN1OctetString(password), controls); 268 269 Validator.ensureNotNull(password); 270 } 271 272 273 274 /** 275 * Creates a new SASL PLAIN bind request with the provided authentication ID, 276 * password, and set of controls. It will not include an authorization ID. 277 * 278 * @param authenticationID The authentication ID for this bind request. It 279 * must not be {@code null}. 280 * @param password The password for this bind request. It must not 281 * be {@code null}. 282 * @param controls The set of controls to include 283 */ 284 public PLAINBindRequest(@NotNull final String authenticationID, 285 @NotNull final byte[] password, 286 @Nullable final Control... controls) 287 { 288 this(authenticationID, null, new ASN1OctetString(password), controls); 289 290 Validator.ensureNotNull(password); 291 } 292 293 294 295 /** 296 * Creates a new SASL PLAIN bind request with the provided authentication ID, 297 * password, and set of controls. It will not include an authorization ID. 298 * 299 * @param authenticationID The authentication ID for this bind request. It 300 * must not be {@code null}. 301 * @param password The password for this bind request. It must not 302 * be {@code null}. 303 * @param controls The set of controls to include 304 */ 305 public PLAINBindRequest(@NotNull final String authenticationID, 306 @NotNull final ASN1OctetString password, 307 @Nullable final Control... controls) 308 { 309 this(authenticationID, null, password, controls); 310 } 311 312 313 314 /** 315 * Creates a new SASL PLAIN bind request with the provided information. 316 * 317 * @param authenticationID The authentication ID for this bind request. It 318 * must not be {@code null}. 319 * @param authorizationID The authorization ID for this bind request, or 320 * {@code null} if there is to be no authorization 321 * ID. 322 * @param password The password for this bind request. It must not 323 * be {@code null}. 324 * @param controls The set of controls to include 325 */ 326 public PLAINBindRequest(@NotNull final String authenticationID, 327 @Nullable final String authorizationID, 328 @NotNull final String password, 329 @Nullable final Control... controls) 330 { 331 this(authenticationID, authorizationID, new ASN1OctetString(password), 332 controls); 333 334 Validator.ensureNotNull(password); 335 } 336 337 338 339 /** 340 * Creates a new SASL PLAIN bind request with the provided information. 341 * 342 * @param authenticationID The authentication ID for this bind request. It 343 * must not be {@code null}. 344 * @param authorizationID The authorization ID for this bind request, or 345 * {@code null} if there is to be no authorization 346 * ID. 347 * @param password The password for this bind request. It must not 348 * be {@code null}. 349 * @param controls The set of controls to include 350 */ 351 public PLAINBindRequest(@NotNull final String authenticationID, 352 @Nullable final String authorizationID, 353 @NotNull final byte[] password, 354 @Nullable final Control... controls) 355 { 356 this(authenticationID, authorizationID, new ASN1OctetString(password), 357 controls); 358 359 Validator.ensureNotNull(password); 360 } 361 362 363 364 /** 365 * Creates a new SASL PLAIN bind request with the provided information. 366 * 367 * @param authenticationID The authentication ID for this bind request. It 368 * must not be {@code null}. 369 * @param authorizationID The authorization ID for this bind request, or 370 * {@code null} if there is to be no authorization 371 * ID. 372 * @param password The password for this bind request. It must not 373 * be {@code null}. 374 * @param controls The set of controls to include 375 */ 376 public PLAINBindRequest(@NotNull final String authenticationID, 377 @Nullable final String authorizationID, 378 @NotNull final ASN1OctetString password, 379 @Nullable final Control... controls) 380 { 381 super(controls); 382 383 Validator.ensureNotNull(authenticationID, password); 384 385 this.authenticationID = authenticationID; 386 this.authorizationID = authorizationID; 387 this.password = password; 388 } 389 390 391 392 /** 393 * {@inheritDoc} 394 */ 395 @Override() 396 @NotNull() 397 public String getSASLMechanismName() 398 { 399 return PLAIN_MECHANISM_NAME; 400 } 401 402 403 404 /** 405 * Retrieves the authentication ID for this bind request. 406 * 407 * @return The authentication ID for this bind request. 408 */ 409 @NotNull() 410 public String getAuthenticationID() 411 { 412 return authenticationID; 413 } 414 415 416 417 /** 418 * Retrieves the authorization ID for this bind request. 419 * 420 * @return The authorization ID for this bind request, or {@code null} if 421 * there is no authorization ID. 422 */ 423 @Nullable() 424 public String getAuthorizationID() 425 { 426 return authorizationID; 427 } 428 429 430 431 /** 432 * Retrieves the string representation of the password for this bind request. 433 * 434 * @return The string representation of the password for this bind request. 435 */ 436 @NotNull() 437 public String getPasswordString() 438 { 439 return password.stringValue(); 440 } 441 442 443 444 /** 445 * Retrieves the bytes that comprise the the password for this bind request. 446 * 447 * @return The bytes that comprise the password for this bind request. 448 */ 449 @NotNull() 450 public byte[] getPasswordBytes() 451 { 452 return password.getValue(); 453 } 454 455 456 457 /** 458 * Sends this bind request to the target server over the provided connection 459 * and returns the corresponding response. 460 * 461 * @param connection The connection to use to send this bind request to the 462 * server and read the associated response. 463 * @param depth The current referral depth for this request. It should 464 * always be one for the initial request, and should only 465 * be incremented when following referrals. 466 * 467 * @return The bind response read from the server. 468 * 469 * @throws LDAPException If a problem occurs while sending the request or 470 * reading the response. 471 */ 472 @Override() 473 @NotNull() 474 protected BindResult process(@NotNull final LDAPConnection connection, 475 final int depth) 476 throws LDAPException 477 { 478 setReferralDepth(depth); 479 480 return sendBindRequest(connection, "", 481 encodeCredentials(authenticationID, authorizationID, password), 482 getControls(), getResponseTimeoutMillis(connection)); 483 } 484 485 486 487 /** 488 * Encodes the provided information into an ASN.1 octet string that may be 489 * used as the SASL credentials for an UnboundID delivered one-time password 490 * bind request. 491 * 492 * @param authenticationID The authentication identity for the bind request. 493 * It must not be {@code null} and must in the form 494 * "u:" followed by a username, or "dn:" followed 495 * by a DN. 496 * @param authorizationID The authorization identity for the bind request. 497 * It may be {@code null} if the authorization 498 * identity should be the same as the authentication 499 * identity. If an authorization identity is 500 * specified, it must be in the form "u:" followed 501 * by a username, or "dn:" followed by a DN. The 502 * value "dn:" may be used to indicate the 503 * authorization identity of the anonymous user. 504 * @param password The password for this PLAIN bind request. It 505 * must not be {@code null}. 506 * 507 * @return An ASN.1 octet string that may be used as the SASL credentials for 508 * an UnboundID delivered one-time password bind request. 509 */ 510 @NotNull() 511 public static ASN1OctetString encodeCredentials( 512 @NotNull final String authenticationID, 513 @Nullable final String authorizationID, 514 @NotNull final ASN1OctetString password) 515 { 516 final byte[] authZIDBytes = StaticUtils.getBytes(authorizationID); 517 final byte[] authNIDBytes = StaticUtils.getBytes(authenticationID); 518 final byte[] passwordBytes = password.getValue(); 519 final byte[] credBytes = new byte[2 + authZIDBytes.length + 520 authNIDBytes.length + passwordBytes.length]; 521 522 System.arraycopy(authZIDBytes, 0, credBytes, 0, authZIDBytes.length); 523 524 int pos = authZIDBytes.length + 1; 525 System.arraycopy(authNIDBytes, 0, credBytes, pos, authNIDBytes.length); 526 527 pos += authNIDBytes.length + 1; 528 System.arraycopy(passwordBytes, 0, credBytes, pos, passwordBytes.length); 529 530 return new ASN1OctetString(credBytes); 531 } 532 533 534 535 /** 536 * {@inheritDoc} 537 */ 538 @Override() 539 @NotNull() 540 public PLAINBindRequest getRebindRequest(@NotNull final String host, 541 final int port) 542 { 543 return new PLAINBindRequest(authenticationID, authorizationID, password, 544 getControls()); 545 } 546 547 548 549 /** 550 * {@inheritDoc} 551 */ 552 @Override() 553 @NotNull() 554 public PLAINBindRequest duplicate() 555 { 556 return duplicate(getControls()); 557 } 558 559 560 561 /** 562 * {@inheritDoc} 563 */ 564 @Override() 565 @NotNull() 566 public PLAINBindRequest duplicate(@Nullable final Control[] controls) 567 { 568 final PLAINBindRequest bindRequest = new PLAINBindRequest(authenticationID, 569 authorizationID, password, controls); 570 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 571 bindRequest.setIntermediateResponseListener( 572 getIntermediateResponseListener()); 573 bindRequest.setReferralDepth(getReferralDepth()); 574 bindRequest.setReferralConnector(getReferralConnectorInternal()); 575 return bindRequest; 576 } 577 578 579 580 /** 581 * {@inheritDoc} 582 */ 583 @Override() 584 public void toString(@NotNull final StringBuilder buffer) 585 { 586 buffer.append("PLAINBindRequest(authenticationID='"); 587 buffer.append(authenticationID); 588 buffer.append('\''); 589 590 if (authorizationID != null) 591 { 592 buffer.append(", authorizationID='"); 593 buffer.append(authorizationID); 594 buffer.append('\''); 595 } 596 597 final Control[] controls = getControls(); 598 if (controls.length > 0) 599 { 600 buffer.append(", controls={"); 601 for (int i=0; i < controls.length; i++) 602 { 603 if (i > 0) 604 { 605 buffer.append(", "); 606 } 607 608 buffer.append(controls[i]); 609 } 610 buffer.append('}'); 611 } 612 613 buffer.append(')'); 614 } 615 616 617 618 /** 619 * {@inheritDoc} 620 */ 621 @Override() 622 public void toCode(@NotNull final List<String> lineList, 623 @NotNull final String requestID, 624 final int indentSpaces, final boolean includeProcessing) 625 { 626 // Create the request variable. 627 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(4); 628 constructorArgs.add(ToCodeArgHelper.createString(authenticationID, 629 "Authentication ID")); 630 constructorArgs.add(ToCodeArgHelper.createString(authorizationID, 631 "Authorization ID")); 632 constructorArgs.add(ToCodeArgHelper.createString("---redacted-password---", 633 "Bind Password")); 634 635 final Control[] controls = getControls(); 636 if (controls.length > 0) 637 { 638 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 639 "Bind Controls")); 640 } 641 642 ToCodeHelper.generateMethodCall(lineList, indentSpaces, "PLAINBindRequest", 643 requestID + "Request", "new PLAINBindRequest", constructorArgs); 644 645 646 // Add lines for processing the request and obtaining the result. 647 if (includeProcessing) 648 { 649 // Generate a string with the appropriate indent. 650 final StringBuilder buffer = new StringBuilder(); 651 for (int i=0; i < indentSpaces; i++) 652 { 653 buffer.append(' '); 654 } 655 final String indent = buffer.toString(); 656 657 lineList.add(""); 658 lineList.add(indent + "try"); 659 lineList.add(indent + '{'); 660 lineList.add(indent + " BindResult " + requestID + 661 "Result = connection.bind(" + requestID + "Request);"); 662 lineList.add(indent + " // The bind was processed successfully."); 663 lineList.add(indent + '}'); 664 lineList.add(indent + "catch (LDAPException e)"); 665 lineList.add(indent + '{'); 666 lineList.add(indent + " // The bind failed. Maybe the following will " + 667 "help explain why."); 668 lineList.add(indent + " // Note that the connection is now likely in " + 669 "an unauthenticated state."); 670 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 671 lineList.add(indent + " String message = e.getMessage();"); 672 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 673 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 674 lineList.add(indent + " Control[] responseControls = " + 675 "e.getResponseControls();"); 676 lineList.add(indent + '}'); 677 } 678 } 679}