001/* 002 * Copyright 2011-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2011-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) 2011-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.util; 037 038 039 040import java.util.ArrayList; 041import java.util.Collections; 042import java.util.HashMap; 043import java.util.List; 044import java.util.Map; 045import java.util.TreeMap; 046 047import com.unboundid.ldap.sdk.ANONYMOUSBindRequest; 048import com.unboundid.ldap.sdk.Control; 049import com.unboundid.ldap.sdk.CRAMMD5BindRequest; 050import com.unboundid.ldap.sdk.DIGESTMD5BindRequest; 051import com.unboundid.ldap.sdk.DIGESTMD5BindRequestProperties; 052import com.unboundid.ldap.sdk.EXTERNALBindRequest; 053import com.unboundid.ldap.sdk.GSSAPIBindRequest; 054import com.unboundid.ldap.sdk.GSSAPIBindRequestProperties; 055import com.unboundid.ldap.sdk.LDAPException; 056import com.unboundid.ldap.sdk.OAUTHBEARERBindRequest; 057import com.unboundid.ldap.sdk.PLAINBindRequest; 058import com.unboundid.ldap.sdk.ResultCode; 059import com.unboundid.ldap.sdk.SASLBindRequest; 060import com.unboundid.ldap.sdk.SASLQualityOfProtection; 061import com.unboundid.ldap.sdk.SCRAMSHA1BindRequest; 062import com.unboundid.ldap.sdk.SCRAMSHA256BindRequest; 063import com.unboundid.ldap.sdk.SCRAMSHA512BindRequest; 064import com.unboundid.ldap.sdk.unboundidds.SingleUseTOTPBindRequest; 065import com.unboundid.ldap.sdk.unboundidds. 066 UnboundIDCertificatePlusPasswordBindRequest; 067import com.unboundid.ldap.sdk.unboundidds.UnboundIDDeliveredOTPBindRequest; 068import com.unboundid.ldap.sdk.unboundidds.UnboundIDTOTPBindRequest; 069import com.unboundid.ldap.sdk.unboundidds.UnboundIDYubiKeyOTPBindRequest; 070 071import static com.unboundid.util.UtilityMessages.*; 072 073 074 075/** 076 * This class provides a utility that may be used to help process SASL bind 077 * operations using the LDAP SDK. 078 */ 079@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 080public final class SASLUtils 081{ 082 /** 083 * The name of the SASL option that specifies the access token. It may be 084 * used in conjunction with the OAUTHBEARER mechanism. 085 */ 086 @NotNull public static final String SASL_OPTION_ACCESS_TOKEN = "accessToken"; 087 088 089 090 /** 091 * The name of the SASL option that specifies the authentication ID. It may 092 * be used in conjunction with the CRAM-MD5, DIGEST-MD5, GSSAPI, and PLAIN 093 * mechanisms. 094 */ 095 @NotNull public static final String SASL_OPTION_AUTH_ID = "authID"; 096 097 098 099 /** 100 * The name of the SASL option that specifies the authorization ID. It may 101 * be used in conjunction with the DIGEST-MD5, GSSAPI, and PLAIN mechanisms. 102 */ 103 @NotNull public static final String SASL_OPTION_AUTHZ_ID = "authzID"; 104 105 106 107 /** 108 * The name of the SASL option that specifies the path to the JAAS config 109 * file. It may be used in conjunction with the GSSAPI mechanism. 110 */ 111 @NotNull public static final String SASL_OPTION_CONFIG_FILE = "configFile"; 112 113 114 115 /** 116 * The name of the SASL option that indicates whether debugging should be 117 * enabled. It may be used in conjunction with the GSSAPI mechanism. 118 */ 119 @NotNull public static final String SASL_OPTION_DEBUG = "debug"; 120 121 122 123 /** 124 * The name of the SASL option that specifies the KDC address. It may be used 125 * in conjunction with the GSSAPI mechanism. 126 */ 127 @NotNull public static final String SASL_OPTION_KDC_ADDRESS = "kdcAddress"; 128 129 130 131 /** 132 * The name of the SASL option that specifies the desired SASL mechanism to 133 * use to authenticate to the server. 134 */ 135 @NotNull public static final String SASL_OPTION_MECHANISM = "mech"; 136 137 138 139 /** 140 * The name of the SASL option that specifies a one-time password. It may be 141 * used in conjunction with the UNBOUNDID-DELIVERED-OTP and 142 * UNBOUNDID-YUBIKEY-OTP mechanisms. 143 */ 144 @NotNull public static final String SASL_OPTION_OTP = "otp"; 145 146 147 148 /** 149 * The name of the SASL option that may be used to indicate whether to 150 * prompt for a static password. It may be used in conjunction with the 151 * UNBOUNDID-TOTP and UNBOUNDID-YUBIKEY-OTP mechanisms. 152 */ 153 @NotNull public static final String SASL_OPTION_PROMPT_FOR_STATIC_PW = 154 "promptForStaticPassword"; 155 156 157 158 /** 159 * The name of the SASL option that specifies the GSSAPI service principal 160 * protocol. It may be used in conjunction with the GSSAPI mechanism. 161 */ 162 @NotNull public static final String SASL_OPTION_PROTOCOL = "protocol"; 163 164 165 166 /** 167 * The name of the SASL option that specifies the quality of protection that 168 * should be used for communication that occurs after the authentication has 169 * completed. 170 */ 171 @NotNull public static final String SASL_OPTION_QOP = "qop"; 172 173 174 175 /** 176 * The name of the SASL option that specifies the realm name. It may be used 177 * in conjunction with the DIGEST-MD5 and GSSAPI mechanisms. 178 */ 179 @NotNull public static final String SASL_OPTION_REALM = "realm"; 180 181 182 183 /** 184 * The name of the SASL option that indicates whether to require an existing 185 * Kerberos session from the ticket cache. It may be used in conjunction with 186 * the GSSAPI mechanism. 187 */ 188 @NotNull public static final String SASL_OPTION_REQUIRE_CACHE = 189 "requireCache"; 190 191 192 193 /** 194 * The name of the SASL option that indicates whether to attempt to renew the 195 * Kerberos TGT for an existing session. It may be used in conjunction with 196 * the GSSAPI mechanism. 197 */ 198 @NotNull public static final String SASL_OPTION_RENEW_TGT = "renewTGT"; 199 200 201 202 /** 203 * The name of the SASL option that specifies the path to the Kerberos ticket 204 * cache to use. It may be used in conjunction with the GSSAPI mechanism. 205 */ 206 @NotNull public static final String SASL_OPTION_TICKET_CACHE_PATH = 207 "ticketCache"; 208 209 210 211 /** 212 * The name of the SASL option that specifies the TOTP authentication code. 213 * It may be used in conjunction with the UNBOUNDID-TOTP mechanism. 214 */ 215 @NotNull public static final String SASL_OPTION_TOTP_PASSWORD = 216 "totpPassword"; 217 218 219 220 /** 221 * The name of the SASL option that specifies the trace string. It may be 222 * used in conjunction with the ANONYMOUS mechanism. 223 */ 224 @NotNull public static final String SASL_OPTION_TRACE = "trace"; 225 226 227 228 /** 229 * The name of the SASL option that specifies the username. It may be 230 * used in conjunction with the SCRAM-SHA-1, SCRAM-SHA-256, and SCRAM-SHA-512 231 * mechanisms. 232 */ 233 @NotNull public static final String SASL_OPTION_USERNAME = "username"; 234 235 236 237 /** 238 * The name of the SASL option that specifies whether to use a Kerberos ticket 239 * cache. It may be used in conjunction with the GSSAPI mechanism. 240 */ 241 @NotNull public static final String SASL_OPTION_USE_TICKET_CACHE = 242 "useTicketCache"; 243 244 245 246 /** 247 * A map with information about all supported SASL mechanisms, mapped from 248 * lowercase mechanism name to an object with information about that 249 * mechanism. 250 */ 251 @NotNull private static final Map<String,SASLMechanismInfo> SASL_MECHANISMS; 252 253 254 255 static 256 { 257 final TreeMap<String,SASLMechanismInfo> m = new TreeMap<>(); 258 259 m.put( 260 StaticUtils.toLowerCase(ANONYMOUSBindRequest.ANONYMOUS_MECHANISM_NAME), 261 new SASLMechanismInfo(ANONYMOUSBindRequest.ANONYMOUS_MECHANISM_NAME, 262 INFO_SASL_ANONYMOUS_DESCRIPTION.get(), false, false, 263 new SASLOption(SASL_OPTION_TRACE, 264 INFO_SASL_ANONYMOUS_OPTION_TRACE.get(), false, false))); 265 266 m.put(StaticUtils.toLowerCase(CRAMMD5BindRequest.CRAMMD5_MECHANISM_NAME), 267 new SASLMechanismInfo(CRAMMD5BindRequest.CRAMMD5_MECHANISM_NAME, 268 INFO_SASL_CRAM_MD5_DESCRIPTION.get(), true, true, 269 new SASLOption(SASL_OPTION_AUTH_ID, 270 INFO_SASL_CRAM_MD5_OPTION_AUTH_ID.get(), true, false))); 271 272 m.put( 273 StaticUtils.toLowerCase(DIGESTMD5BindRequest.DIGESTMD5_MECHANISM_NAME), 274 new SASLMechanismInfo(DIGESTMD5BindRequest.DIGESTMD5_MECHANISM_NAME, 275 INFO_SASL_DIGEST_MD5_DESCRIPTION.get(), true, true, 276 new SASLOption(SASL_OPTION_AUTH_ID, 277 INFO_SASL_DIGEST_MD5_OPTION_AUTH_ID.get(), true, false), 278 new SASLOption(SASL_OPTION_AUTHZ_ID, 279 INFO_SASL_DIGEST_MD5_OPTION_AUTHZ_ID.get(), false, false), 280 new SASLOption(SASL_OPTION_REALM, 281 INFO_SASL_DIGEST_MD5_OPTION_REALM.get(), false, false), 282 new SASLOption(SASL_OPTION_QOP, 283 INFO_SASL_DIGEST_MD5_OPTION_QOP.get(), false, false))); 284 285 m.put(StaticUtils.toLowerCase(EXTERNALBindRequest.EXTERNAL_MECHANISM_NAME), 286 new SASLMechanismInfo(EXTERNALBindRequest.EXTERNAL_MECHANISM_NAME, 287 INFO_SASL_EXTERNAL_DESCRIPTION.get(), false, false)); 288 289 m.put(StaticUtils.toLowerCase(GSSAPIBindRequest.GSSAPI_MECHANISM_NAME), 290 new SASLMechanismInfo(GSSAPIBindRequest.GSSAPI_MECHANISM_NAME, 291 INFO_SASL_GSSAPI_DESCRIPTION.get(), true, false, 292 new SASLOption(SASL_OPTION_AUTH_ID, 293 INFO_SASL_GSSAPI_OPTION_AUTH_ID.get(), true, false), 294 new SASLOption(SASL_OPTION_AUTHZ_ID, 295 INFO_SASL_GSSAPI_OPTION_AUTHZ_ID.get(), false, false), 296 new SASLOption(SASL_OPTION_CONFIG_FILE, 297 INFO_SASL_GSSAPI_OPTION_CONFIG_FILE.get(), false, false), 298 new SASLOption(SASL_OPTION_DEBUG, 299 INFO_SASL_GSSAPI_OPTION_DEBUG.get(), false, false), 300 new SASLOption(SASL_OPTION_KDC_ADDRESS, 301 INFO_SASL_GSSAPI_OPTION_KDC_ADDRESS.get(), false, false), 302 new SASLOption(SASL_OPTION_PROTOCOL, 303 INFO_SASL_GSSAPI_OPTION_PROTOCOL.get(), false, false), 304 new SASLOption(SASL_OPTION_REALM, 305 INFO_SASL_GSSAPI_OPTION_REALM.get(), false, false), 306 new SASLOption(SASL_OPTION_QOP, 307 INFO_SASL_GSSAPI_OPTION_QOP.get(), false, false), 308 new SASLOption(SASL_OPTION_RENEW_TGT, 309 INFO_SASL_GSSAPI_OPTION_RENEW_TGT.get(), false, false), 310 new SASLOption(SASL_OPTION_REQUIRE_CACHE, 311 INFO_SASL_GSSAPI_OPTION_REQUIRE_TICKET_CACHE.get(), false, 312 false), 313 new SASLOption(SASL_OPTION_TICKET_CACHE_PATH, 314 INFO_SASL_GSSAPI_OPTION_TICKET_CACHE.get(), false, false), 315 new SASLOption(SASL_OPTION_USE_TICKET_CACHE, 316 INFO_SASL_GSSAPI_OPTION_USE_TICKET_CACHE.get(), false, 317 false))); 318 319 m.put(StaticUtils.toLowerCase( 320 OAUTHBEARERBindRequest.OAUTHBEARER_MECHANISM_NAME), 321 new SASLMechanismInfo( 322 OAUTHBEARERBindRequest.OAUTHBEARER_MECHANISM_NAME, 323 INFO_SASL_PLAIN_DESCRIPTION.get(), false, false, 324 new SASLOption(SASL_OPTION_ACCESS_TOKEN, 325 INFO_SASL_OAUTHBEARER_OPTION_ACCESS_TOKEN.get(), false, 326 false))); 327 328 m.put(StaticUtils.toLowerCase(PLAINBindRequest.PLAIN_MECHANISM_NAME), 329 new SASLMechanismInfo(PLAINBindRequest.PLAIN_MECHANISM_NAME, 330 INFO_SASL_PLAIN_DESCRIPTION.get(), true, true, 331 new SASLOption(SASL_OPTION_AUTH_ID, 332 INFO_SASL_PLAIN_OPTION_AUTH_ID.get(), true, false), 333 new SASLOption(SASL_OPTION_AUTHZ_ID, 334 INFO_SASL_PLAIN_OPTION_AUTHZ_ID.get(), false, false))); 335 336 m.put( 337 StaticUtils.toLowerCase( 338 SCRAMSHA1BindRequest.SCRAM_SHA_1_MECHANISM_NAME), 339 new SASLMechanismInfo(SCRAMSHA1BindRequest.SCRAM_SHA_1_MECHANISM_NAME, 340 INFO_SASL_SCRAM_SHA_1_DESCRIPTION.get(), true, true, 341 new SASLOption(SASL_OPTION_USERNAME, 342 INFO_SASL_SCRAM_OPTION_USERNAME.get(), true, false))); 343 344 m.put( 345 StaticUtils.toLowerCase( 346 SCRAMSHA256BindRequest.SCRAM_SHA_256_MECHANISM_NAME), 347 new SASLMechanismInfo( 348 SCRAMSHA256BindRequest.SCRAM_SHA_256_MECHANISM_NAME, 349 INFO_SASL_SCRAM_SHA_256_DESCRIPTION.get(), true, true, 350 new SASLOption(SASL_OPTION_USERNAME, 351 INFO_SASL_SCRAM_OPTION_USERNAME.get(), true, false))); 352 353 m.put( 354 StaticUtils.toLowerCase( 355 SCRAMSHA512BindRequest.SCRAM_SHA_512_MECHANISM_NAME), 356 new SASLMechanismInfo( 357 SCRAMSHA512BindRequest.SCRAM_SHA_512_MECHANISM_NAME, 358 INFO_SASL_SCRAM_SHA_512_DESCRIPTION.get(), true, true, 359 new SASLOption(SASL_OPTION_USERNAME, 360 INFO_SASL_SCRAM_OPTION_USERNAME.get(), true, false))); 361 362 m.put(StaticUtils.toLowerCase( 363 UnboundIDCertificatePlusPasswordBindRequest. 364 UNBOUNDID_CERT_PLUS_PW_MECHANISM_NAME), 365 new SASLMechanismInfo( 366 UnboundIDCertificatePlusPasswordBindRequest. 367 UNBOUNDID_CERT_PLUS_PW_MECHANISM_NAME, 368 INFO_SASL_UNBOUNDID_CERT_PLUS_PASSWORD_DESCRIPTION.get(), true, 369 true)); 370 371 m.put( 372 StaticUtils.toLowerCase( 373 UnboundIDDeliveredOTPBindRequest. 374 UNBOUNDID_DELIVERED_OTP_MECHANISM_NAME), 375 new SASLMechanismInfo( 376 UnboundIDDeliveredOTPBindRequest. 377 UNBOUNDID_DELIVERED_OTP_MECHANISM_NAME, 378 INFO_SASL_UNBOUNDID_DELIVERED_OTP_DESCRIPTION.get(), false, false, 379 new SASLOption(SASL_OPTION_AUTH_ID, 380 INFO_SASL_UNBOUNDID_TOTP_OPTION_AUTH_ID.get(), true, false), 381 new SASLOption(SASL_OPTION_AUTHZ_ID, 382 INFO_SASL_UNBOUNDID_TOTP_OPTION_AUTHZ_ID.get(), false, 383 false), 384 new SASLOption(SASL_OPTION_OTP, 385 INFO_SASL_UNBOUNDID_DELIVERED_OTP_OPTION_OTP.get(), true, 386 false))); 387 388 m.put( 389 StaticUtils.toLowerCase( 390 UnboundIDTOTPBindRequest.UNBOUNDID_TOTP_MECHANISM_NAME), 391 new SASLMechanismInfo( 392 UnboundIDTOTPBindRequest.UNBOUNDID_TOTP_MECHANISM_NAME, 393 INFO_SASL_UNBOUNDID_TOTP_DESCRIPTION.get(), true, false, 394 new SASLOption(SASL_OPTION_AUTH_ID, 395 INFO_SASL_UNBOUNDID_TOTP_OPTION_AUTH_ID.get(), true, false), 396 new SASLOption(SASL_OPTION_AUTHZ_ID, 397 INFO_SASL_UNBOUNDID_TOTP_OPTION_AUTHZ_ID.get(), false, 398 false), 399 new SASLOption(SASL_OPTION_PROMPT_FOR_STATIC_PW, 400 INFO_SASL_UNBOUNDID_TOTP_OPTION_PROMPT_FOR_PW.get(), false, 401 false), 402 new SASLOption(SASL_OPTION_TOTP_PASSWORD, 403 INFO_SASL_UNBOUNDID_TOTP_OPTION_TOTP_PASSWORD.get(), true, 404 false))); 405 406 m.put( 407 StaticUtils.toLowerCase( 408 UnboundIDYubiKeyOTPBindRequest. 409 UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME), 410 new SASLMechanismInfo( 411 UnboundIDYubiKeyOTPBindRequest. 412 UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME, 413 INFO_SASL_UNBOUNDID_YUBIKEY_OTP_DESCRIPTION.get(), true, false, 414 new SASLOption(SASL_OPTION_AUTH_ID, 415 INFO_SASL_UNBOUNDID_YUBIKEY_OTP_OPTION_AUTH_ID.get(), true, 416 false), 417 new SASLOption(SASL_OPTION_AUTHZ_ID, 418 INFO_SASL_UNBOUNDID_YUBIKEY_OTP_OPTION_AUTHZ_ID.get(), false, 419 false), 420 new SASLOption(SASL_OPTION_OTP, 421 INFO_SASL_UNBOUNDID_YUBIKEY_OTP_OPTION_OTP.get(), true, 422 false), 423 new SASLOption(SASL_OPTION_PROMPT_FOR_STATIC_PW, 424 INFO_SASL_UNBOUNDID_YUBIKEY_OTP_OPTION_PROMPT_FOR_PW.get(), 425 false, false))); 426 427 SASL_MECHANISMS = Collections.unmodifiableMap(m); 428 } 429 430 431 432 /** 433 * Prevent this utility class from being instantiated. 434 */ 435 private SASLUtils() 436 { 437 // No implementation required. 438 } 439 440 441 442 /** 443 * Retrieves information about the SASL mechanisms supported for use by this 444 * class. 445 * 446 * @return Information about the SASL mechanisms supported for use by this 447 * class. 448 */ 449 @NotNull() 450 public static List<SASLMechanismInfo> getSupportedSASLMechanisms() 451 { 452 return Collections.unmodifiableList( 453 new ArrayList<>(SASL_MECHANISMS.values())); 454 } 455 456 457 458 /** 459 * Retrieves information about the specified SASL mechanism. 460 * 461 * @param mechanism The name of the SASL mechanism for which to retrieve 462 * information. It will be treated in a case-insensitive 463 * manner. 464 * 465 * @return Information about the requested SASL mechanism, or {@code null} if 466 * no information about the specified mechanism is available. 467 */ 468 @Nullable() 469 public static SASLMechanismInfo getSASLMechanismInfo( 470 @NotNull final String mechanism) 471 { 472 return SASL_MECHANISMS.get(StaticUtils.toLowerCase(mechanism)); 473 } 474 475 476 477 /** 478 * Creates a new SASL bind request using the provided information. 479 * 480 * @param bindDN The bind DN to use for the SASL bind request. For most 481 * SASL mechanisms, this should be {@code null}, since the 482 * identity of the target user should be specified in some 483 * other way (e.g., via an "authID" SASL option). 484 * @param password The password to use for the SASL bind request. It may 485 * be {@code null} if no password is required for the 486 * desired SASL mechanism. 487 * @param mechanism The name of the SASL mechanism to use. It may be 488 * {@code null} if the provided set of options contains a 489 * "mech" option to specify the desired SASL option. 490 * @param options The set of SASL options to use when creating the bind 491 * request, in the form "name=value". It may be 492 * {@code null} or empty if no SASL options are needed and 493 * a value was provided for the {@code mechanism} argument. 494 * If the set of SASL options includes a "mech" option, 495 * then the {@code mechanism} argument must be {@code null} 496 * or have a value that matches the value of the "mech" 497 * SASL option. 498 * 499 * @return The SASL bind request created using the provided information. 500 * 501 * @throws LDAPException If a problem is encountered while trying to create 502 * the SASL bind request. 503 */ 504 @NotNull() 505 public static SASLBindRequest createBindRequest(@Nullable final String bindDN, 506 @Nullable final String password, 507 @Nullable final String mechanism, 508 @Nullable final String... options) 509 throws LDAPException 510 { 511 return createBindRequest(bindDN, 512 (password == null ? null : StaticUtils.getBytes(password)), mechanism, 513 StaticUtils.toList(options)); 514 } 515 516 517 518 /** 519 * Creates a new SASL bind request using the provided information. 520 * 521 * @param bindDN The bind DN to use for the SASL bind request. For most 522 * SASL mechanisms, this should be {@code null}, since the 523 * identity of the target user should be specified in some 524 * other way (e.g., via an "authID" SASL option). 525 * @param password The password to use for the SASL bind request. It may 526 * be {@code null} if no password is required for the 527 * desired SASL mechanism. 528 * @param mechanism The name of the SASL mechanism to use. It may be 529 * {@code null} if the provided set of options contains a 530 * "mech" option to specify the desired SASL option. 531 * @param options The set of SASL options to use when creating the bind 532 * request, in the form "name=value". It may be 533 * {@code null} or empty if no SASL options are needed and 534 * a value was provided for the {@code mechanism} argument. 535 * If the set of SASL options includes a "mech" option, 536 * then the {@code mechanism} argument must be {@code null} 537 * or have a value that matches the value of the "mech" 538 * SASL option. 539 * @param controls The set of controls to include in the request. 540 * 541 * @return The SASL bind request created using the provided information. 542 * 543 * @throws LDAPException If a problem is encountered while trying to create 544 * the SASL bind request. 545 */ 546 @NotNull() 547 public static SASLBindRequest createBindRequest(@Nullable final String bindDN, 548 @Nullable final String password, 549 @Nullable final String mechanism, 550 @Nullable final List<String> options, 551 @Nullable final Control... controls) 552 throws LDAPException 553 { 554 return createBindRequest(bindDN, 555 (password == null 556 ? null 557 : StaticUtils.getBytes(password)), mechanism, options, 558 controls); 559 } 560 561 562 563 /** 564 * Creates a new SASL bind request using the provided information. 565 * 566 * @param bindDN The bind DN to use for the SASL bind request. For most 567 * SASL mechanisms, this should be {@code null}, since the 568 * identity of the target user should be specified in some 569 * other way (e.g., via an "authID" SASL option). 570 * @param password The password to use for the SASL bind request. It may 571 * be {@code null} if no password is required for the 572 * desired SASL mechanism. 573 * @param mechanism The name of the SASL mechanism to use. It may be 574 * {@code null} if the provided set of options contains a 575 * "mech" option to specify the desired SASL option. 576 * @param options The set of SASL options to use when creating the bind 577 * request, in the form "name=value". It may be 578 * {@code null} or empty if no SASL options are needed and 579 * a value was provided for the {@code mechanism} argument. 580 * If the set of SASL options includes a "mech" option, 581 * then the {@code mechanism} argument must be {@code null} 582 * or have a value that matches the value of the "mech" 583 * SASL option. 584 * 585 * @return The SASL bind request created using the provided information. 586 * 587 * @throws LDAPException If a problem is encountered while trying to create 588 * the SASL bind request. 589 */ 590 @NotNull() 591 public static SASLBindRequest createBindRequest(@Nullable final String bindDN, 592 @Nullable final byte[] password, 593 @Nullable final String mechanism, 594 @Nullable final String... options) 595 throws LDAPException 596 { 597 return createBindRequest(bindDN, password, mechanism, 598 StaticUtils.toList(options)); 599 } 600 601 602 603 /** 604 * Creates a new SASL bind request using the provided information. 605 * 606 * @param bindDN The bind DN to use for the SASL bind request. For most 607 * SASL mechanisms, this should be {@code null}, since the 608 * identity of the target user should be specified in some 609 * other way (e.g., via an "authID" SASL option). 610 * @param password The password to use for the SASL bind request. It may 611 * be {@code null} if no password is required for the 612 * desired SASL mechanism. 613 * @param mechanism The name of the SASL mechanism to use. It may be 614 * {@code null} if the provided set of options contains a 615 * "mech" option to specify the desired SASL option. 616 * @param options The set of SASL options to use when creating the bind 617 * request, in the form "name=value". It may be 618 * {@code null} or empty if no SASL options are needed and 619 * a value was provided for the {@code mechanism} argument. 620 * If the set of SASL options includes a "mech" option, 621 * then the {@code mechanism} argument must be {@code null} 622 * or have a value that matches the value of the "mech" 623 * SASL option. 624 * @param controls The set of controls to include in the request. 625 * 626 * @return The SASL bind request created using the provided information. 627 * 628 * @throws LDAPException If a problem is encountered while trying to create 629 * the SASL bind request. 630 */ 631 @NotNull() 632 public static SASLBindRequest createBindRequest(@Nullable final String bindDN, 633 @Nullable final byte[] password, 634 @Nullable final String mechanism, 635 @Nullable final List<String> options, 636 @Nullable final Control... controls) 637 throws LDAPException 638 { 639 return createBindRequest(bindDN, password, false, null, mechanism, options, 640 controls); 641 } 642 643 644 645 /** 646 * Creates a new SASL bind request using the provided information. 647 * 648 * @param bindDN The bind DN to use for the SASL bind request. 649 * For most SASL mechanisms, this should be 650 * {@code null}, since the identity of the target 651 * user should be specified in some other way 652 * (e.g., via an "authID" SASL option). 653 * @param password The password to use for the SASL bind request. 654 * It may be {@code null} if no password is 655 * required for the desired SASL mechanism. 656 * @param promptForPassword Indicates whether to interactively prompt for 657 * the password if one is needed but none was 658 * provided. 659 * @param tool The command-line tool whose input and output 660 * streams should be used when prompting for the 661 * bind password. It may be {@code null} if 662 * {@code promptForPassword} is {@code false}. 663 * @param mechanism The name of the SASL mechanism to use. It may 664 * be {@code null} if the provided set of options 665 * contains a "mech" option to specify the desired 666 * SASL option. 667 * @param options The set of SASL options to use when creating the 668 * bind request, in the form "name=value". It may 669 * be {@code null} or empty if no SASL options are 670 * needed and a value was provided for the 671 * {@code mechanism} argument. If the set of SASL 672 * options includes a "mech" option, then the 673 * {@code mechanism} argument must be {@code null} 674 * or have a value that matches the value of the 675 * "mech" SASL option. 676 * @param controls The set of controls to include in the request. 677 * 678 * @return The SASL bind request created using the provided information. 679 * 680 * @throws LDAPException If a problem is encountered while trying to create 681 * the SASL bind request. 682 */ 683 @NotNull() 684 public static SASLBindRequest createBindRequest(@Nullable final String bindDN, 685 @Nullable final byte[] password, 686 final boolean promptForPassword, 687 @Nullable final CommandLineTool tool, 688 @Nullable final String mechanism, 689 @Nullable final List<String> options, 690 @Nullable final Control... controls) 691 throws LDAPException 692 { 693 if (promptForPassword) 694 { 695 Validator.ensureNotNull(tool); 696 } 697 698 // Parse the provided set of options to ensure that they are properly 699 // formatted in name-value form, and extract the SASL mechanism. 700 final String mech; 701 final Map<String,String> optionsMap = parseOptions(options); 702 final String mechOption = 703 optionsMap.remove(StaticUtils.toLowerCase(SASL_OPTION_MECHANISM)); 704 if (mechOption != null) 705 { 706 mech = mechOption; 707 if ((mechanism != null) && (! mech.equalsIgnoreCase(mechanism))) 708 { 709 throw new LDAPException(ResultCode.PARAM_ERROR, 710 ERR_SASL_OPTION_MECH_CONFLICT.get(mechanism, mech)); 711 } 712 } 713 else 714 { 715 mech = mechanism; 716 } 717 718 if (mech == null) 719 { 720 throw new LDAPException(ResultCode.PARAM_ERROR, 721 ERR_SASL_OPTION_NO_MECH.get()); 722 } 723 724 if (mech.equalsIgnoreCase(ANONYMOUSBindRequest.ANONYMOUS_MECHANISM_NAME)) 725 { 726 return createANONYMOUSBindRequest(password, optionsMap, controls); 727 } 728 else if (mech.equalsIgnoreCase(CRAMMD5BindRequest.CRAMMD5_MECHANISM_NAME)) 729 { 730 return createCRAMMD5BindRequest(password, promptForPassword, tool, 731 optionsMap, controls); 732 } 733 else if (mech.equalsIgnoreCase( 734 DIGESTMD5BindRequest.DIGESTMD5_MECHANISM_NAME)) 735 { 736 return createDIGESTMD5BindRequest(password, promptForPassword, tool, 737 optionsMap, controls); 738 } 739 else if (mech.equalsIgnoreCase(EXTERNALBindRequest.EXTERNAL_MECHANISM_NAME)) 740 { 741 return createEXTERNALBindRequest(password, optionsMap, controls); 742 } 743 else if (mech.equalsIgnoreCase(GSSAPIBindRequest.GSSAPI_MECHANISM_NAME)) 744 { 745 return createGSSAPIBindRequest(password, promptForPassword, tool, 746 optionsMap, controls); 747 } 748 else if (mech.equalsIgnoreCase( 749 OAUTHBEARERBindRequest.OAUTHBEARER_MECHANISM_NAME)) 750 { 751 return createOAUTHBEARERBindRequest(password, promptForPassword, 752 tool, optionsMap, controls); 753 } 754 else if (mech.equalsIgnoreCase(PLAINBindRequest.PLAIN_MECHANISM_NAME)) 755 { 756 return createPLAINBindRequest(password, promptForPassword, tool, 757 optionsMap, controls); 758 } 759 else if (mech.equalsIgnoreCase( 760 SCRAMSHA1BindRequest.SCRAM_SHA_1_MECHANISM_NAME)) 761 { 762 return createSCRAMSHA1BindRequest(password, promptForPassword, tool, 763 optionsMap, controls); 764 } 765 else if (mech.equalsIgnoreCase( 766 SCRAMSHA256BindRequest.SCRAM_SHA_256_MECHANISM_NAME)) 767 { 768 return createSCRAMSHA256BindRequest(password, promptForPassword, tool, 769 optionsMap, controls); 770 } 771 else if (mech.equalsIgnoreCase( 772 SCRAMSHA512BindRequest.SCRAM_SHA_512_MECHANISM_NAME)) 773 { 774 return createSCRAMSHA512BindRequest(password, promptForPassword, tool, 775 optionsMap, controls); 776 } 777 else if (mech.equalsIgnoreCase(UnboundIDCertificatePlusPasswordBindRequest. 778 UNBOUNDID_CERT_PLUS_PW_MECHANISM_NAME)) 779 { 780 return createUnboundIDCertificatePlusPasswordBindRequest(password, tool, 781 optionsMap, controls); 782 } 783 else if (mech.equalsIgnoreCase(UnboundIDDeliveredOTPBindRequest. 784 UNBOUNDID_DELIVERED_OTP_MECHANISM_NAME)) 785 { 786 return createUNBOUNDIDDeliveredOTPBindRequest(password, optionsMap, 787 controls); 788 } 789 else if (mech.equalsIgnoreCase( 790 UnboundIDTOTPBindRequest.UNBOUNDID_TOTP_MECHANISM_NAME)) 791 { 792 return createUNBOUNDIDTOTPBindRequest(password, tool, optionsMap, 793 controls); 794 } 795 else if (mech.equalsIgnoreCase( 796 UnboundIDYubiKeyOTPBindRequest.UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME)) 797 { 798 return createUNBOUNDIDYUBIKEYOTPBindRequest(password, tool, optionsMap, 799 controls); 800 } 801 else 802 { 803 throw new LDAPException(ResultCode.PARAM_ERROR, 804 ERR_SASL_OPTION_UNSUPPORTED_MECH.get(mech)); 805 } 806 } 807 808 809 810 /** 811 * Creates a SASL ANONYMOUS bind request using the provided set of options. 812 * 813 * @param password The password to use for the bind request. 814 * @param options The set of SASL options for the bind request. 815 * @param controls The set of controls to include in the request. 816 * 817 * @return The SASL ANONYMOUS bind request that was created. 818 * 819 * @throws LDAPException If a problem is encountered while trying to create 820 * the SASL bind request. 821 */ 822 @NotNull() 823 private static ANONYMOUSBindRequest createANONYMOUSBindRequest( 824 @Nullable final byte[] password, 825 @NotNull final Map<String,String> options, 826 @Nullable final Control[] controls) 827 throws LDAPException 828 { 829 if (password != null) 830 { 831 throw new LDAPException(ResultCode.PARAM_ERROR, 832 ERR_SASL_OPTION_MECH_DOESNT_ACCEPT_PASSWORD.get( 833 ANONYMOUSBindRequest.ANONYMOUS_MECHANISM_NAME)); 834 } 835 836 837 // The trace option is optional. 838 final String trace = 839 options.remove(StaticUtils.toLowerCase(SASL_OPTION_TRACE)); 840 841 // Ensure no unsupported options were provided. 842 ensureNoUnsupportedOptions(options, 843 ANONYMOUSBindRequest.ANONYMOUS_MECHANISM_NAME); 844 845 return new ANONYMOUSBindRequest(trace, controls); 846 } 847 848 849 850 /** 851 * Creates a SASL CRAM-MD5 bind request using the provided password and set of 852 * options. 853 * 854 * @param password The password to use for the bind request. 855 * @param promptForPassword Indicates whether to interactively prompt for 856 * the password if one is needed but none was 857 * provided. 858 * @param tool The command-line tool whose input and output 859 * streams should be used when prompting for the 860 * bind password. It may be {@code null} only if 861 * {@code promptForPassword} is {@code false}. 862 * @param options The set of SASL options for the bind request. 863 * @param controls The set of controls to include in the request. 864 * 865 * @return The SASL CRAM-MD5 bind request that was created. 866 * 867 * @throws LDAPException If a problem is encountered while trying to create 868 * the SASL bind request. 869 */ 870 @NotNull() 871 private static CRAMMD5BindRequest createCRAMMD5BindRequest( 872 @Nullable final byte[] password, 873 final boolean promptForPassword, 874 @Nullable final CommandLineTool tool, 875 @NotNull final Map<String,String> options, 876 @Nullable final Control[] controls) 877 throws LDAPException 878 { 879 final byte[] pw; 880 if (password == null) 881 { 882 if (promptForPassword) 883 { 884 tool.getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 885 pw = PasswordReader.readPassword(); 886 tool.getOriginalOut().println(); 887 } 888 else 889 { 890 throw new LDAPException(ResultCode.PARAM_ERROR, 891 ERR_SASL_OPTION_MECH_REQUIRES_PASSWORD.get( 892 CRAMMD5BindRequest.CRAMMD5_MECHANISM_NAME)); 893 } 894 } 895 else 896 { 897 pw = password; 898 } 899 900 901 // The authID option is required. 902 final String authID = 903 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTH_ID)); 904 if (authID == null) 905 { 906 throw new LDAPException(ResultCode.PARAM_ERROR, 907 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_AUTH_ID, 908 CRAMMD5BindRequest.CRAMMD5_MECHANISM_NAME)); 909 } 910 911 912 // Ensure no unsupported options were provided. 913 ensureNoUnsupportedOptions(options, 914 CRAMMD5BindRequest.CRAMMD5_MECHANISM_NAME); 915 916 return new CRAMMD5BindRequest(authID, pw, controls); 917 } 918 919 920 921 /** 922 * Creates a SASL DIGEST-MD5 bind request using the provided password and set 923 * of options. 924 * 925 * @param password The password to use for the bind request. 926 * @param promptForPassword Indicates whether to interactively prompt for 927 * the password if one is needed but none was 928 * provided. 929 * @param tool The command-line tool whose input and output 930 * streams should be used when prompting for the 931 * bind password. It may be {@code null} only if 932 * {@code promptForPassword} is {@code false}. 933 * @param options The set of SASL options for the bind request. 934 * @param controls The set of controls to include in the request. 935 * 936 * @return The SASL DIGEST-MD5 bind request that was created. 937 * 938 * @throws LDAPException If a problem is encountered while trying to create 939 * the SASL bind request. 940 */ 941 @NotNull() 942 private static DIGESTMD5BindRequest createDIGESTMD5BindRequest( 943 @Nullable() final byte[] password, 944 final boolean promptForPassword, 945 @Nullable final CommandLineTool tool, 946 @NotNull final Map<String,String> options, 947 @Nullable final Control[] controls) 948 throws LDAPException 949 { 950 final byte[] pw; 951 if (password == null) 952 { 953 if (promptForPassword) 954 { 955 tool.getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 956 pw = PasswordReader.readPassword(); 957 tool.getOriginalOut().println(); 958 } 959 else 960 { 961 throw new LDAPException(ResultCode.PARAM_ERROR, 962 ERR_SASL_OPTION_MECH_REQUIRES_PASSWORD.get( 963 DIGESTMD5BindRequest.DIGESTMD5_MECHANISM_NAME)); 964 } 965 } 966 else 967 { 968 pw = password; 969 } 970 971 // The authID option is required. 972 final String authID = 973 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTH_ID)); 974 if (authID == null) 975 { 976 throw new LDAPException(ResultCode.PARAM_ERROR, 977 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_AUTH_ID, 978 DIGESTMD5BindRequest.DIGESTMD5_MECHANISM_NAME)); 979 } 980 981 final DIGESTMD5BindRequestProperties properties = 982 new DIGESTMD5BindRequestProperties(authID, pw); 983 984 // The authzID option is optional. 985 properties.setAuthorizationID( 986 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTHZ_ID))); 987 988 // The realm option is optional. 989 properties.setRealm(options.remove( 990 StaticUtils.toLowerCase(SASL_OPTION_REALM))); 991 992 // The QoP option is optional, and may contain multiple values that need to 993 // be parsed. 994 final String qopString = 995 options.remove(StaticUtils.toLowerCase(SASL_OPTION_QOP)); 996 if (qopString != null) 997 { 998 properties.setAllowedQoP( 999 SASLQualityOfProtection.decodeQoPList(qopString)); 1000 } 1001 1002 // Ensure no unsupported options were provided. 1003 ensureNoUnsupportedOptions(options, 1004 DIGESTMD5BindRequest.DIGESTMD5_MECHANISM_NAME); 1005 1006 return new DIGESTMD5BindRequest(properties, controls); 1007 } 1008 1009 1010 1011 /** 1012 * Creates a SASL EXTERNAL bind request using the provided set of options. 1013 * 1014 * @param password The password to use for the bind request. 1015 * @param options The set of SASL options for the bind request. 1016 * @param controls The set of controls to include in the request. 1017 * 1018 * @return The SASL EXTERNAL bind request that was created. 1019 * 1020 * @throws LDAPException If a problem is encountered while trying to create 1021 * the SASL bind request. 1022 */ 1023 @NotNull() 1024 private static EXTERNALBindRequest createEXTERNALBindRequest( 1025 @Nullable final byte[] password, 1026 @NotNull final Map<String,String> options, 1027 @Nullable final Control[] controls) 1028 throws LDAPException 1029 { 1030 if (password != null) 1031 { 1032 throw new LDAPException(ResultCode.PARAM_ERROR, 1033 ERR_SASL_OPTION_MECH_DOESNT_ACCEPT_PASSWORD.get( 1034 EXTERNALBindRequest.EXTERNAL_MECHANISM_NAME)); 1035 } 1036 1037 // Ensure no unsupported options were provided. 1038 ensureNoUnsupportedOptions(options, 1039 EXTERNALBindRequest.EXTERNAL_MECHANISM_NAME); 1040 1041 return new EXTERNALBindRequest(controls); 1042 } 1043 1044 1045 1046 /** 1047 * Creates a SASL GSSAPI bind request using the provided password and set of 1048 * options. 1049 * 1050 * @param password The password to use for the bind request. 1051 * @param promptForPassword Indicates whether to interactively prompt for 1052 * the password if one is needed but none was 1053 * provided. 1054 * @param tool The command-line tool whose input and output 1055 * streams should be used when prompting for the 1056 * bind password. It may be {@code null} only if 1057 * {@code promptForPassword} is {@code false}. 1058 * @param options The set of SASL options for the bind request. 1059 * @param controls The set of controls to include in the request. 1060 * 1061 * @return The SASL GSSAPI bind request that was created. 1062 * 1063 * @throws LDAPException If a problem is encountered while trying to create 1064 * the SASL bind request. 1065 */ 1066 @NotNull() 1067 private static GSSAPIBindRequest createGSSAPIBindRequest( 1068 @Nullable final byte[] password, 1069 final boolean promptForPassword, 1070 @Nullable final CommandLineTool tool, 1071 @NotNull final Map<String,String> options, 1072 @Nullable final Control[] controls) 1073 throws LDAPException 1074 { 1075 // The authID option is required. 1076 final String authID = 1077 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTH_ID)); 1078 if (authID == null) 1079 { 1080 throw new LDAPException(ResultCode.PARAM_ERROR, 1081 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_AUTH_ID, 1082 GSSAPIBindRequest.GSSAPI_MECHANISM_NAME)); 1083 } 1084 final GSSAPIBindRequestProperties gssapiProperties = 1085 new GSSAPIBindRequestProperties(authID, password); 1086 1087 // The authzID option is optional. 1088 gssapiProperties.setAuthorizationID( 1089 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTHZ_ID))); 1090 1091 // The configFile option is optional. 1092 gssapiProperties.setConfigFilePath(options.remove( 1093 StaticUtils.toLowerCase(SASL_OPTION_CONFIG_FILE))); 1094 1095 // The debug option is optional. 1096 gssapiProperties.setEnableGSSAPIDebugging(getBooleanValue(options, 1097 SASL_OPTION_DEBUG, false)); 1098 1099 // The kdcAddress option is optional. 1100 gssapiProperties.setKDCAddress(options.remove( 1101 StaticUtils.toLowerCase(SASL_OPTION_KDC_ADDRESS))); 1102 1103 // The protocol option is optional. 1104 final String protocol = 1105 options.remove(StaticUtils.toLowerCase(SASL_OPTION_PROTOCOL)); 1106 if (protocol != null) 1107 { 1108 gssapiProperties.setServicePrincipalProtocol(protocol); 1109 } 1110 1111 // The realm option is optional. 1112 gssapiProperties.setRealm(options.remove( 1113 StaticUtils.toLowerCase(SASL_OPTION_REALM))); 1114 1115 // The QoP option is optional, and may contain multiple values that need to 1116 // be parsed. 1117 final String qopString = 1118 options.remove(StaticUtils.toLowerCase(SASL_OPTION_QOP)); 1119 if (qopString != null) 1120 { 1121 gssapiProperties.setAllowedQoP( 1122 SASLQualityOfProtection.decodeQoPList(qopString)); 1123 } 1124 1125 // The renewTGT option is optional. 1126 gssapiProperties.setRenewTGT(getBooleanValue(options, SASL_OPTION_RENEW_TGT, 1127 false)); 1128 1129 // The requireCache option is optional. 1130 gssapiProperties.setRequireCachedCredentials(getBooleanValue(options, 1131 SASL_OPTION_REQUIRE_CACHE, false)); 1132 1133 // The ticketCache option is optional. 1134 gssapiProperties.setTicketCachePath(options.remove( 1135 StaticUtils.toLowerCase(SASL_OPTION_TICKET_CACHE_PATH))); 1136 1137 // The useTicketCache option is optional. 1138 gssapiProperties.setUseTicketCache(getBooleanValue(options, 1139 SASL_OPTION_USE_TICKET_CACHE, true)); 1140 1141 // Ensure no unsupported options were provided. 1142 ensureNoUnsupportedOptions(options, 1143 GSSAPIBindRequest.GSSAPI_MECHANISM_NAME); 1144 1145 // A password must have been provided unless useTicketCache=true and 1146 // requireTicketCache=true. 1147 if (password == null) 1148 { 1149 if (! (gssapiProperties.useTicketCache() && 1150 gssapiProperties.requireCachedCredentials())) 1151 { 1152 if (promptForPassword) 1153 { 1154 tool.getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 1155 gssapiProperties.setPassword(PasswordReader.readPassword()); 1156 tool.getOriginalOut().println(); 1157 } 1158 else 1159 { 1160 throw new LDAPException(ResultCode.PARAM_ERROR, 1161 ERR_SASL_OPTION_GSSAPI_PASSWORD_REQUIRED.get()); 1162 } 1163 } 1164 } 1165 1166 return new GSSAPIBindRequest(gssapiProperties, controls); 1167 } 1168 1169 1170 1171 /** 1172 * Creates a SASL OAUTHBEARER bind request using the provided password and 1173 * set of options. 1174 * 1175 * @param password The password to use for the bind request. 1176 * @param promptForPassword Indicates whether to interactively prompt for 1177 * the password if one is needed but none was 1178 * provided. 1179 * @param tool The command-line tool whose input and output 1180 * streams should be used when prompting for the 1181 * bind password. It may be {@code null} only if 1182 * {@code promptForPassword} is {@code false}. 1183 * @param options The set of SASL options for the bind request. 1184 * @param controls The set of controls to include in the request. 1185 * 1186 * @return The SASL OAUTHBEARER bind request that was created. 1187 * 1188 * @throws LDAPException If a problem is encountered while trying to create 1189 * the SASL bind request. 1190 */ 1191 @NotNull() 1192 private static OAUTHBEARERBindRequest createOAUTHBEARERBindRequest( 1193 @Nullable final byte[] password, 1194 final boolean promptForPassword, 1195 @Nullable final CommandLineTool tool, 1196 @NotNull final Map<String,String> options, 1197 @Nullable final Control[] controls) 1198 throws LDAPException 1199 { 1200 // The accessToken option wasn't declared as required, but we will either 1201 // require it to have been provided or we will interactively prompt for it. 1202 String accessToken = 1203 options.remove(StaticUtils.toLowerCase(SASL_OPTION_ACCESS_TOKEN)); 1204 if (accessToken == null) 1205 { 1206 if (promptForPassword) 1207 { 1208 tool.getOriginalOut().print( 1209 INFO_SASL_TOOL_ENTER_OAUTHBEARER_ACCESS_TOKEN.get()); 1210 accessToken = StaticUtils.toUTF8String(PasswordReader.readPassword()); 1211 tool.getOriginalOut().println(); 1212 } 1213 else 1214 { 1215 throw new LDAPException(ResultCode.PARAM_ERROR, 1216 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_ACCESS_TOKEN, 1217 OAUTHBEARERBindRequest.OAUTHBEARER_MECHANISM_NAME)); 1218 } 1219 } 1220 1221 // Ensure no unsupported options were provided. 1222 ensureNoUnsupportedOptions(options, 1223 OAUTHBEARERBindRequest.OAUTHBEARER_MECHANISM_NAME); 1224 1225 return new OAUTHBEARERBindRequest(accessToken, controls); 1226 } 1227 1228 1229 1230 /** 1231 * Creates a SASL PLAIN bind request using the provided password and set of 1232 * options. 1233 * 1234 * @param password The password to use for the bind request. 1235 * @param promptForPassword Indicates whether to interactively prompt for 1236 * the password if one is needed but none was 1237 * provided. 1238 * @param tool The command-line tool whose input and output 1239 * streams should be used when prompting for the 1240 * bind password. It may be {@code null} only if 1241 * {@code promptForPassword} is {@code false}. 1242 * @param options The set of SASL options for the bind request. 1243 * @param controls The set of controls to include in the request. 1244 * 1245 * @return The SASL PLAIN bind request that was created. 1246 * 1247 * @throws LDAPException If a problem is encountered while trying to create 1248 * the SASL bind request. 1249 */ 1250 @NotNull() 1251 private static PLAINBindRequest createPLAINBindRequest( 1252 @Nullable final byte[] password, 1253 final boolean promptForPassword, 1254 @Nullable final CommandLineTool tool, 1255 @NotNull final Map<String,String> options, 1256 @Nullable final Control[] controls) 1257 throws LDAPException 1258 { 1259 final byte[] pw; 1260 if (password == null) 1261 { 1262 if (promptForPassword) 1263 { 1264 tool.getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 1265 pw = PasswordReader.readPassword(); 1266 tool.getOriginalOut().println(); 1267 } 1268 else 1269 { 1270 throw new LDAPException(ResultCode.PARAM_ERROR, 1271 ERR_SASL_OPTION_MECH_REQUIRES_PASSWORD.get( 1272 PLAINBindRequest.PLAIN_MECHANISM_NAME)); 1273 } 1274 } 1275 else 1276 { 1277 pw = password; 1278 } 1279 1280 // The authID option is required. 1281 final String authID = 1282 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTH_ID)); 1283 if (authID == null) 1284 { 1285 throw new LDAPException(ResultCode.PARAM_ERROR, 1286 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_AUTH_ID, 1287 PLAINBindRequest.PLAIN_MECHANISM_NAME)); 1288 } 1289 1290 // The authzID option is optional. 1291 final String authzID = 1292 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTHZ_ID)); 1293 1294 // Ensure no unsupported options were provided. 1295 ensureNoUnsupportedOptions(options, 1296 PLAINBindRequest.PLAIN_MECHANISM_NAME); 1297 1298 return new PLAINBindRequest(authID, authzID, pw, controls); 1299 } 1300 1301 1302 1303 /** 1304 * Creates a SASL SCRAM-SHA-1 bind request using the provided password and 1305 * set of options. 1306 * 1307 * @param password The password to use for the bind request. 1308 * @param promptForPassword Indicates whether to interactively prompt for 1309 * the password if one is needed but none was 1310 * provided. 1311 * @param tool The command-line tool whose input and output 1312 * streams should be used when prompting for the 1313 * bind password. It may be {@code null} only if 1314 * {@code promptForPassword} is {@code false}. 1315 * @param options The set of SASL options for the bind request. 1316 * @param controls The set of controls to include in the request. 1317 * 1318 * @return The SASL SCRAM-SHA-1 bind request that was created. 1319 * 1320 * @throws LDAPException If a problem is encountered while trying to create 1321 * the SASL bind request. 1322 */ 1323 @NotNull() 1324 private static SCRAMSHA1BindRequest createSCRAMSHA1BindRequest( 1325 @Nullable final byte[] password, 1326 final boolean promptForPassword, 1327 @Nullable final CommandLineTool tool, 1328 @NotNull final Map<String,String> options, 1329 @Nullable final Control[] controls) 1330 throws LDAPException 1331 { 1332 final byte[] pw; 1333 if (password == null) 1334 { 1335 if (promptForPassword) 1336 { 1337 tool.getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 1338 pw = PasswordReader.readPassword(); 1339 tool.getOriginalOut().println(); 1340 } 1341 else 1342 { 1343 throw new LDAPException(ResultCode.PARAM_ERROR, 1344 ERR_SASL_OPTION_MECH_REQUIRES_PASSWORD.get( 1345 SCRAMSHA1BindRequest.SCRAM_SHA_1_MECHANISM_NAME)); 1346 } 1347 } 1348 else 1349 { 1350 pw = password; 1351 } 1352 1353 // The username option is required. 1354 final String username = 1355 options.remove(StaticUtils.toLowerCase(SASL_OPTION_USERNAME)); 1356 if (username == null) 1357 { 1358 throw new LDAPException(ResultCode.PARAM_ERROR, 1359 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_USERNAME, 1360 SCRAMSHA1BindRequest.SCRAM_SHA_1_MECHANISM_NAME)); 1361 } 1362 1363 // Ensure no unsupported options were provided. 1364 ensureNoUnsupportedOptions(options, 1365 SCRAMSHA1BindRequest.SCRAM_SHA_1_MECHANISM_NAME); 1366 1367 return new SCRAMSHA1BindRequest(username, pw, controls); 1368 } 1369 1370 1371 1372 /** 1373 * Creates a SASL SCRAM-SHA-256 bind request using the provided password and 1374 * set of options. 1375 * 1376 * @param password The password to use for the bind request. 1377 * @param promptForPassword Indicates whether to interactively prompt for 1378 * the password if one is needed but none was 1379 * provided. 1380 * @param tool The command-line tool whose input and output 1381 * streams should be used when prompting for the 1382 * bind password. It may be {@code null} only if 1383 * {@code promptForPassword} is {@code false}. 1384 * @param options The set of SASL options for the bind request. 1385 * @param controls The set of controls to include in the request. 1386 * 1387 * @return The SASL SCRAM-SHA-256 bind request that was created. 1388 * 1389 * @throws LDAPException If a problem is encountered while trying to create 1390 * the SASL bind request. 1391 */ 1392 @NotNull() 1393 private static SCRAMSHA256BindRequest createSCRAMSHA256BindRequest( 1394 @Nullable final byte[] password, 1395 final boolean promptForPassword, 1396 @Nullable final CommandLineTool tool, 1397 @NotNull final Map<String,String> options, 1398 @Nullable final Control[] controls) 1399 throws LDAPException 1400 { 1401 final byte[] pw; 1402 if (password == null) 1403 { 1404 if (promptForPassword) 1405 { 1406 tool.getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 1407 pw = PasswordReader.readPassword(); 1408 tool.getOriginalOut().println(); 1409 } 1410 else 1411 { 1412 throw new LDAPException(ResultCode.PARAM_ERROR, 1413 ERR_SASL_OPTION_MECH_REQUIRES_PASSWORD.get( 1414 SCRAMSHA256BindRequest.SCRAM_SHA_256_MECHANISM_NAME)); 1415 } 1416 } 1417 else 1418 { 1419 pw = password; 1420 } 1421 1422 // The username option is required. 1423 final String username = 1424 options.remove(StaticUtils.toLowerCase(SASL_OPTION_USERNAME)); 1425 if (username == null) 1426 { 1427 throw new LDAPException(ResultCode.PARAM_ERROR, 1428 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_USERNAME, 1429 SCRAMSHA256BindRequest.SCRAM_SHA_256_MECHANISM_NAME)); 1430 } 1431 1432 // Ensure no unsupported options were provided. 1433 ensureNoUnsupportedOptions(options, 1434 SCRAMSHA256BindRequest.SCRAM_SHA_256_MECHANISM_NAME); 1435 1436 return new SCRAMSHA256BindRequest(username, pw, controls); 1437 } 1438 1439 1440 1441 /** 1442 * Creates a SASL SCRAM-SHA-512 bind request using the provided password and 1443 * set of options. 1444 * 1445 * @param password The password to use for the bind request. 1446 * @param promptForPassword Indicates whether to interactively prompt for 1447 * the password if one is needed but none was 1448 * provided. 1449 * @param tool The command-line tool whose input and output 1450 * streams should be used when prompting for the 1451 * bind password. It may be {@code null} only if 1452 * {@code promptForPassword} is {@code false}. 1453 * @param options The set of SASL options for the bind request. 1454 * @param controls The set of controls to include in the request. 1455 * 1456 * @return The SASL SCRAM-SHA-512 bind request that was created. 1457 * 1458 * @throws LDAPException If a problem is encountered while trying to create 1459 * the SASL bind request. 1460 */ 1461 @NotNull() 1462 private static SCRAMSHA512BindRequest createSCRAMSHA512BindRequest( 1463 @Nullable final byte[] password, 1464 final boolean promptForPassword, 1465 @Nullable final CommandLineTool tool, 1466 @NotNull final Map<String,String> options, 1467 @Nullable final Control[] controls) 1468 throws LDAPException 1469 { 1470 final byte[] pw; 1471 if (password == null) 1472 { 1473 if (promptForPassword) 1474 { 1475 tool.getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 1476 pw = PasswordReader.readPassword(); 1477 tool.getOriginalOut().println(); 1478 } 1479 else 1480 { 1481 throw new LDAPException(ResultCode.PARAM_ERROR, 1482 ERR_SASL_OPTION_MECH_REQUIRES_PASSWORD.get( 1483 SCRAMSHA512BindRequest.SCRAM_SHA_512_MECHANISM_NAME)); 1484 } 1485 } 1486 else 1487 { 1488 pw = password; 1489 } 1490 1491 // The username option is required. 1492 final String username = 1493 options.remove(StaticUtils.toLowerCase(SASL_OPTION_USERNAME)); 1494 if (username == null) 1495 { 1496 throw new LDAPException(ResultCode.PARAM_ERROR, 1497 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_USERNAME, 1498 SCRAMSHA512BindRequest.SCRAM_SHA_512_MECHANISM_NAME)); 1499 } 1500 1501 // Ensure no unsupported options were provided. 1502 ensureNoUnsupportedOptions(options, 1503 SCRAMSHA512BindRequest.SCRAM_SHA_512_MECHANISM_NAME); 1504 1505 return new SCRAMSHA512BindRequest(username, pw, controls); 1506 } 1507 1508 1509 1510 /** 1511 * Creates a SASL UNBOUNDID-CERTIFICATE-PLUS-PASSWORD bind request using the 1512 * provided set of options. 1513 * 1514 * @param password The password to use for the bind request. 1515 * @param tool The command-line tool whose input and output streams 1516 * should be used when prompting for the bind password. It 1517 * may be {@code null} only if {@code promptForPassword} is 1518 * {@code false}. 1519 * @param options The set of SASL options for the bind request. 1520 * @param controls The set of controls to include in the request. 1521 * 1522 * @return The SASL UNBOUNDID-CERTIFICATE-PLUS-PASSWORD bind request that was 1523 * created. 1524 * 1525 * @throws LDAPException If a problem is encountered while trying to create 1526 * the SASL bind request. 1527 */ 1528 @NotNull() 1529 private static UnboundIDCertificatePlusPasswordBindRequest 1530 createUnboundIDCertificatePlusPasswordBindRequest( 1531 @Nullable final byte[] password, 1532 @Nullable final CommandLineTool tool, 1533 @NotNull final Map<String,String> options, 1534 @Nullable final Control[] controls) 1535 throws LDAPException 1536 { 1537 final byte[] pw; 1538 if (password == null) 1539 { 1540 tool.getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 1541 pw = PasswordReader.readPassword(); 1542 tool.getOriginalOut().println(); 1543 } 1544 else 1545 { 1546 pw = password; 1547 } 1548 1549 // Ensure no unsupported options were provided. 1550 ensureNoUnsupportedOptions(options, 1551 UnboundIDCertificatePlusPasswordBindRequest. 1552 UNBOUNDID_CERT_PLUS_PW_MECHANISM_NAME); 1553 1554 return new UnboundIDCertificatePlusPasswordBindRequest(pw, controls); 1555 } 1556 1557 1558 1559 /** 1560 * Creates a SASL UNBOUNDID-DELIVERED-OTP bind request using the provided 1561 * password and set of options. 1562 * 1563 * @param password The password to use for the bind request. 1564 * @param options The set of SASL options for the bind request. 1565 * @param controls The set of controls to include in the request. 1566 * 1567 * @return The SASL UNBOUNDID-DELIVERED-OTP bind request that was created. 1568 * 1569 * @throws LDAPException If a problem is encountered while trying to create 1570 * the SASL bind request. 1571 */ 1572 @NotNull() 1573 private static UnboundIDDeliveredOTPBindRequest 1574 createUNBOUNDIDDeliveredOTPBindRequest( 1575 @Nullable final byte[] password, 1576 @NotNull final Map<String,String> options, 1577 @Nullable final Control... controls) 1578 throws LDAPException 1579 { 1580 if (password != null) 1581 { 1582 throw new LDAPException(ResultCode.PARAM_ERROR, 1583 ERR_SASL_OPTION_MECH_DOESNT_ACCEPT_PASSWORD.get( 1584 UnboundIDDeliveredOTPBindRequest. 1585 UNBOUNDID_DELIVERED_OTP_MECHANISM_NAME)); 1586 } 1587 1588 // The authID option is required. 1589 final String authID = 1590 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTH_ID)); 1591 if (authID == null) 1592 { 1593 throw new LDAPException(ResultCode.PARAM_ERROR, 1594 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_AUTH_ID, 1595 UnboundIDDeliveredOTPBindRequest. 1596 UNBOUNDID_DELIVERED_OTP_MECHANISM_NAME)); 1597 } 1598 1599 // The OTP option is required. 1600 final String otp = options.remove(StaticUtils.toLowerCase(SASL_OPTION_OTP)); 1601 if (otp == null) 1602 { 1603 throw new LDAPException(ResultCode.PARAM_ERROR, 1604 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_OTP, 1605 UnboundIDDeliveredOTPBindRequest. 1606 UNBOUNDID_DELIVERED_OTP_MECHANISM_NAME)); 1607 } 1608 1609 // The authzID option is optional. 1610 final String authzID = 1611 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTHZ_ID)); 1612 1613 // Ensure no unsupported options were provided. 1614 ensureNoUnsupportedOptions(options, 1615 UnboundIDDeliveredOTPBindRequest. 1616 UNBOUNDID_DELIVERED_OTP_MECHANISM_NAME); 1617 1618 return new UnboundIDDeliveredOTPBindRequest(authID, authzID, otp, controls); 1619 } 1620 1621 1622 1623 /** 1624 * Creates a SASL UNBOUNDID-TOTP bind request using the provided password and 1625 * set of options. 1626 * 1627 * @param password The password to use for the bind request. 1628 * @param tool The command-line tool whose input and output streams 1629 * should be used when prompting for the bind password. It 1630 * may be {@code null} only if {@code promptForPassword} is 1631 * {@code false}. 1632 * @param options The set of SASL options for the bind request. 1633 * @param controls The set of controls to include in the request. 1634 * 1635 * @return The SASL UNBOUNDID-TOTP bind request that was created. 1636 * 1637 * @throws LDAPException If a problem is encountered while trying to create 1638 * the SASL bind request. 1639 */ 1640 @NotNull() 1641 private static SingleUseTOTPBindRequest createUNBOUNDIDTOTPBindRequest( 1642 @Nullable final byte[] password, 1643 @Nullable final CommandLineTool tool, 1644 @NotNull final Map<String,String> options, 1645 @Nullable final Control... controls) 1646 throws LDAPException 1647 { 1648 // The authID option is required. 1649 final String authID = 1650 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTH_ID)); 1651 if (authID == null) 1652 { 1653 throw new LDAPException(ResultCode.PARAM_ERROR, 1654 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_AUTH_ID, 1655 UnboundIDTOTPBindRequest.UNBOUNDID_TOTP_MECHANISM_NAME)); 1656 } 1657 1658 // The TOTP password option is required. 1659 final String totpPassword = 1660 options.remove(StaticUtils.toLowerCase(SASL_OPTION_TOTP_PASSWORD)); 1661 if (totpPassword == null) 1662 { 1663 throw new LDAPException(ResultCode.PARAM_ERROR, 1664 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_TOTP_PASSWORD, 1665 UnboundIDTOTPBindRequest.UNBOUNDID_TOTP_MECHANISM_NAME)); 1666 } 1667 1668 // The authzID option is optional. 1669 byte[] pwBytes = password; 1670 final String authzID = 1671 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTHZ_ID)); 1672 1673 // The promptForStaticPassword option is optional. 1674 final String promptStr = options.remove(StaticUtils.toLowerCase( 1675 SASL_OPTION_PROMPT_FOR_STATIC_PW)); 1676 if (promptStr != null) 1677 { 1678 if (promptStr.equalsIgnoreCase("true")) 1679 { 1680 if (pwBytes == null) 1681 { 1682 tool.getOriginalOut().print(INFO_SASL_ENTER_STATIC_PW.get()); 1683 pwBytes = PasswordReader.readPassword(); 1684 tool.getOriginalOut().println(); 1685 } 1686 else 1687 { 1688 throw new LDAPException(ResultCode.PARAM_ERROR, 1689 ERR_SASL_PROMPT_FOR_PROVIDED_PW.get( 1690 SASL_OPTION_PROMPT_FOR_STATIC_PW)); 1691 } 1692 } 1693 else if (! promptStr.equalsIgnoreCase("false")) 1694 { 1695 throw new LDAPException(ResultCode.PARAM_ERROR, 1696 ERR_SASL_PROMPT_FOR_STATIC_PW_BAD_VALUE.get( 1697 SASL_OPTION_PROMPT_FOR_STATIC_PW)); 1698 } 1699 } 1700 1701 // Ensure no unsupported options were provided. 1702 ensureNoUnsupportedOptions(options, 1703 UnboundIDTOTPBindRequest.UNBOUNDID_TOTP_MECHANISM_NAME); 1704 1705 return new SingleUseTOTPBindRequest(authID, authzID, totpPassword, pwBytes, 1706 controls); 1707 } 1708 1709 1710 1711 /** 1712 * Creates a SASL UNBOUNDID-YUBIKEY-OTP bind request using the provided 1713 * password and set of options. 1714 * 1715 * @param password The password to use for the bind request. 1716 * @param tool The command-line tool whose input and output streams 1717 * should be used when prompting for the bind password. It 1718 * may be {@code null} only if {@code promptForPassword} is 1719 * {@code false}. 1720 * @param options The set of SASL options for the bind request. 1721 * @param controls The set of controls to include in the request. 1722 * 1723 * @return The SASL UNBOUNDID-YUBIKEY-OTP bind request that was created. 1724 * 1725 * @throws LDAPException If a problem is encountered while trying to create 1726 * the SASL bind request. 1727 */ 1728 @NotNull() 1729 private static UnboundIDYubiKeyOTPBindRequest 1730 createUNBOUNDIDYUBIKEYOTPBindRequest( 1731 @Nullable final byte[] password, 1732 @Nullable final CommandLineTool tool, 1733 @NotNull final Map<String,String> options, 1734 @Nullable final Control... controls) 1735 throws LDAPException 1736 { 1737 // The authID option is required. 1738 final String authID = 1739 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTH_ID)); 1740 if (authID == null) 1741 { 1742 throw new LDAPException(ResultCode.PARAM_ERROR, 1743 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_AUTH_ID, 1744 UnboundIDYubiKeyOTPBindRequest. 1745 UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME)); 1746 } 1747 1748 // The otp option is required. 1749 final String otp = options.remove(StaticUtils.toLowerCase(SASL_OPTION_OTP)); 1750 if (otp == null) 1751 { 1752 throw new LDAPException(ResultCode.PARAM_ERROR, 1753 ERR_SASL_MISSING_REQUIRED_OPTION.get(SASL_OPTION_OTP, 1754 UnboundIDYubiKeyOTPBindRequest. 1755 UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME)); 1756 } 1757 1758 // The authzID option is optional. 1759 final String authzID = 1760 options.remove(StaticUtils.toLowerCase(SASL_OPTION_AUTHZ_ID)); 1761 1762 // The promptForStaticPassword option is optional. 1763 byte[] pwBytes = password; 1764 final String promptStr = options.remove(StaticUtils.toLowerCase( 1765 SASL_OPTION_PROMPT_FOR_STATIC_PW)); 1766 if (promptStr != null) 1767 { 1768 if (promptStr.equalsIgnoreCase("true")) 1769 { 1770 if (pwBytes == null) 1771 { 1772 tool.getOriginalOut().print(INFO_SASL_ENTER_STATIC_PW.get()); 1773 pwBytes = PasswordReader.readPassword(); 1774 tool.getOriginalOut().println(); 1775 } 1776 else 1777 { 1778 throw new LDAPException(ResultCode.PARAM_ERROR, 1779 ERR_SASL_PROMPT_FOR_PROVIDED_PW.get( 1780 SASL_OPTION_PROMPT_FOR_STATIC_PW)); 1781 } 1782 } 1783 else if (! promptStr.equalsIgnoreCase("false")) 1784 { 1785 throw new LDAPException(ResultCode.PARAM_ERROR, 1786 ERR_SASL_PROMPT_FOR_STATIC_PW_BAD_VALUE.get( 1787 SASL_OPTION_PROMPT_FOR_STATIC_PW)); 1788 } 1789 } 1790 1791 // Ensure no unsupported options were provided. 1792 ensureNoUnsupportedOptions(options, 1793 UnboundIDYubiKeyOTPBindRequest.UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME); 1794 1795 return new UnboundIDYubiKeyOTPBindRequest(authID, authzID, pwBytes, otp, 1796 controls); 1797 } 1798 1799 1800 1801 /** 1802 * Parses the provided list of SASL options. 1803 * 1804 * @param options The list of options to be parsed. 1805 * 1806 * @return A map with the parsed set of options. 1807 * 1808 * @throws LDAPException If a problem is encountered while parsing options. 1809 */ 1810 @NotNull() 1811 private static Map<String,String> 1812 parseOptions(@Nullable final List<String> options) 1813 throws LDAPException 1814 { 1815 if (options == null) 1816 { 1817 return new HashMap<>(0); 1818 } 1819 1820 final HashMap<String,String> m = 1821 new HashMap<>(StaticUtils.computeMapCapacity(options.size())); 1822 for (final String s : options) 1823 { 1824 final int equalPos = s.indexOf('='); 1825 if (equalPos < 0) 1826 { 1827 throw new LDAPException(ResultCode.PARAM_ERROR, 1828 ERR_SASL_OPTION_MISSING_EQUAL.get(s)); 1829 } 1830 else if (equalPos == 0) 1831 { 1832 throw new LDAPException(ResultCode.PARAM_ERROR, 1833 ERR_SASL_OPTION_STARTS_WITH_EQUAL.get(s)); 1834 } 1835 1836 final String name = s.substring(0, equalPos); 1837 final String value = s.substring(equalPos + 1); 1838 if (m.put(StaticUtils.toLowerCase(name), value) != null) 1839 { 1840 throw new LDAPException(ResultCode.PARAM_ERROR, 1841 ERR_SASL_OPTION_NOT_MULTI_VALUED.get(name)); 1842 } 1843 } 1844 1845 return m; 1846 } 1847 1848 1849 1850 /** 1851 * Ensures that the provided map is empty, and will throw an exception if it 1852 * isn't. This method is intended for internal use only. 1853 * 1854 * @param options The map of options to ensure is empty. 1855 * @param mechanism The associated SASL mechanism. 1856 * 1857 * @throws LDAPException If the map of SASL options is not empty. 1858 */ 1859 @InternalUseOnly() 1860 public static void ensureNoUnsupportedOptions( 1861 @NotNull final Map<String,String> options, 1862 @NotNull final String mechanism) 1863 throws LDAPException 1864 { 1865 if (! options.isEmpty()) 1866 { 1867 for (final String s : options.keySet()) 1868 { 1869 throw new LDAPException(ResultCode.PARAM_ERROR, 1870 ERR_SASL_OPTION_UNSUPPORTED_FOR_MECH.get(s,mechanism)); 1871 } 1872 } 1873 } 1874 1875 1876 1877 /** 1878 * Retrieves the value of the specified option and parses it as a boolean. 1879 * Values of "true", "t", "yes", "y", "on", and "1" will be treated as 1880 * {@code true}. Values of "false", "f", "no", "n", "off", and "0" will be 1881 * treated as {@code false}. 1882 * 1883 * @param m The map from which to retrieve the option. It must not be 1884 * {@code null}. 1885 * @param o The name of the option to examine. 1886 * @param d The default value to use if the given option was not provided. 1887 * 1888 * @return The parsed boolean value. 1889 * 1890 * @throws LDAPException If the option value cannot be parsed as a boolean. 1891 */ 1892 static boolean getBooleanValue(@NotNull final Map<String,String> m, 1893 @NotNull final String o, final boolean d) 1894 throws LDAPException 1895 { 1896 final String s = 1897 StaticUtils.toLowerCase(m.remove(StaticUtils.toLowerCase(o))); 1898 if (s == null) 1899 { 1900 return d; 1901 } 1902 else if (s.equals("true") || 1903 s.equals("t") || 1904 s.equals("yes") || 1905 s.equals("y") || 1906 s.equals("on") || 1907 s.equals("1")) 1908 { 1909 return true; 1910 } 1911 else if (s.equals("false") || 1912 s.equals("f") || 1913 s.equals("no") || 1914 s.equals("n") || 1915 s.equals("off") || 1916 s.equals("0")) 1917 { 1918 return false; 1919 } 1920 else 1921 { 1922 throw new LDAPException(ResultCode.PARAM_ERROR, 1923 ERR_SASL_OPTION_MALFORMED_BOOLEAN_VALUE.get(o)); 1924 } 1925 } 1926 1927 1928 1929 /** 1930 * Retrieves a string representation of the SASL usage information. This will 1931 * include the supported SASL mechanisms and the properties that may be used 1932 * with each. 1933 * 1934 * @param maxWidth The maximum line width to use for the output. If this is 1935 * less than or equal to zero, then no wrapping will be 1936 * performed. 1937 * 1938 * @return A string representation of the usage information. 1939 */ 1940 @NotNull() 1941 public static String getUsageString(final int maxWidth) 1942 { 1943 return getUsageString(null, maxWidth); 1944 } 1945 1946 1947 1948 /** 1949 * Retrieves a string representation of the SASL usage information. This will 1950 * include the supported SASL mechanisms and the properties that may be used 1951 * with each. 1952 * 1953 * @param mechanism The name of the SASL mechanism for which to obtain usage 1954 * information It may be {@code null} if usage should be 1955 * displayed for all available mechamisms. 1956 * @param maxWidth The maximum line width to use for the output. If this 1957 * is less than or equal to zero, then no wrapping will be 1958 * performed. 1959 * 1960 * @return A string representation of the usage information. 1961 */ 1962 @NotNull() 1963 public static String getUsageString(@Nullable final String mechanism, 1964 final int maxWidth) 1965 { 1966 final StringBuilder buffer = new StringBuilder(); 1967 1968 for (final String line : getUsage(mechanism, maxWidth)) 1969 { 1970 buffer.append(line); 1971 buffer.append(StaticUtils.EOL); 1972 } 1973 1974 return buffer.toString(); 1975 } 1976 1977 1978 1979 /** 1980 * Retrieves lines that make up the SASL usage information, optionally 1981 * wrapping long lines. 1982 * 1983 * @param maxWidth The maximum line width to use for the output. If this is 1984 * less than or equal to zero, then no wrapping will be 1985 * performed. 1986 * 1987 * @return The lines that make up the SASL usage information. 1988 */ 1989 @NotNull() 1990 public static List<String> getUsage(final int maxWidth) 1991 { 1992 return getUsage(null, maxWidth); 1993 } 1994 1995 1996 1997 /** 1998 * Retrieves lines that make up the SASL usage information, optionally 1999 * wrapping long lines. 2000 * 2001 * @param mechanism The name of the SASL mechanism for which to obtain usage 2002 * information It may be {@code null} if usage should be 2003 * displayed for all available mechamisms. 2004 * @param maxWidth The maximum line width to use for the output. If this 2005 * is less than or equal to zero, then no wrapping will be 2006 * performed. 2007 * 2008 * @return The lines that make up the SASL usage information. 2009 */ 2010 @NotNull() 2011 public static List<String> getUsage(@Nullable final String mechanism, 2012 final int maxWidth) 2013 { 2014 final ArrayList<String> lines = new ArrayList<>(100); 2015 2016 boolean first = true; 2017 for (final SASLMechanismInfo i : getSupportedSASLMechanisms()) 2018 { 2019 if ((mechanism != null) && (! i.getName().equalsIgnoreCase(mechanism))) 2020 { 2021 continue; 2022 } 2023 2024 if (first) 2025 { 2026 first = false; 2027 } 2028 else 2029 { 2030 lines.add(""); 2031 lines.add(""); 2032 } 2033 2034 lines.addAll( 2035 StaticUtils.wrapLine(INFO_SASL_HELP_MECHANISM.get(i.getName()), 2036 maxWidth)); 2037 lines.add(""); 2038 2039 for (final String line : 2040 StaticUtils.wrapLine(i.getDescription(), maxWidth - 4)) 2041 { 2042 lines.add(" " + line); 2043 } 2044 lines.add(""); 2045 2046 for (final String line : 2047 StaticUtils.wrapLine(INFO_SASL_HELP_MECHANISM_OPTIONS.get( 2048 i.getName()), maxWidth - 4)) 2049 { 2050 lines.add(" " + line); 2051 } 2052 2053 if (i.acceptsPassword()) 2054 { 2055 lines.add(""); 2056 if (i.requiresPassword()) 2057 { 2058 for (final String line : 2059 StaticUtils.wrapLine(INFO_SASL_HELP_PASSWORD_REQUIRED.get( 2060 i.getName()), maxWidth - 4)) 2061 { 2062 lines.add(" " + line); 2063 } 2064 } 2065 else 2066 { 2067 for (final String line : 2068 StaticUtils.wrapLine(INFO_SASL_HELP_PASSWORD_OPTIONAL.get( 2069 i.getName()), maxWidth - 4)) 2070 { 2071 lines.add(" " + line); 2072 } 2073 } 2074 } 2075 2076 for (final SASLOption o : i.getOptions()) 2077 { 2078 lines.add(""); 2079 lines.add(" * " + o.getName()); 2080 for (final String line : 2081 StaticUtils.wrapLine(o.getDescription(), maxWidth - 14)) 2082 { 2083 lines.add(" " + line); 2084 } 2085 } 2086 } 2087 2088 if ((mechanism != null) && lines.isEmpty()) 2089 { 2090 return getUsage(null, maxWidth); 2091 } 2092 2093 2094 return lines; 2095 } 2096}