001/* 002 * Copyright 2008-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-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) 2008-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.unboundidds.controls; 037 038 039 040import java.io.Serializable; 041import java.util.ArrayList; 042 043import com.unboundid.asn1.ASN1Boolean; 044import com.unboundid.asn1.ASN1Constants; 045import com.unboundid.asn1.ASN1Element; 046import com.unboundid.asn1.ASN1OctetString; 047import com.unboundid.asn1.ASN1Sequence; 048import com.unboundid.ldap.sdk.LDAPException; 049import com.unboundid.ldap.sdk.ResultCode; 050import com.unboundid.util.Debug; 051import com.unboundid.util.NotMutable; 052import com.unboundid.util.NotNull; 053import com.unboundid.util.Nullable; 054import com.unboundid.util.StaticUtils; 055import com.unboundid.util.ThreadSafety; 056import com.unboundid.util.ThreadSafetyLevel; 057 058import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 059 060 061 062/** 063 * This class implements a data structure which encapsulates the value of an 064 * intermediate client response value. It may recursively embed intermediate 065 * client response values from upstream servers. 066 * <BR> 067 * <BLOCKQUOTE> 068 * <B>NOTE:</B> This class, and other classes within the 069 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 070 * supported for use against Ping Identity, UnboundID, and 071 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 072 * for proprietary functionality or for external specifications that are not 073 * considered stable or mature enough to be guaranteed to work in an 074 * interoperable way with other types of LDAP servers. 075 * </BLOCKQUOTE> 076 * <BR> 077 * See the documentation in the {@link IntermediateClientRequestControl} class 078 * for an example of using the intermediate client request and response 079 * controls. 080 */ 081@NotMutable() 082@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 083public final class IntermediateClientResponseValue 084 implements Serializable 085{ 086 /** 087 * The BER type for the upstreamResponse element. 088 */ 089 private static final byte TYPE_UPSTREAM_RESPONSE = (byte) 0xA0; 090 091 092 093 /** 094 * The BER type for the upstreamServerAddress element. 095 */ 096 private static final byte TYPE_UPSTREAM_SERVER_ADDRESS = (byte) 0x81; 097 098 099 100 /** 101 * The BER type for the upstreamServerSecure element. 102 */ 103 private static final byte TYPE_UPSTREAM_SERVER_SECURE = (byte) 0x82; 104 105 106 107 /** 108 * The BER type for the serverName element. 109 */ 110 private static final byte TYPE_SERVER_NAME = (byte) 0x83; 111 112 113 114 /** 115 * The BER type for the serverSessionID element. 116 */ 117 private static final byte TYPE_SERVER_SESSION_ID = (byte) 0x84; 118 119 120 121 /** 122 * The BER type for the serverResponseID element. 123 */ 124 private static final byte TYPE_SERVER_RESPONSE_ID = (byte) 0x85; 125 126 127 128 /** 129 * The serial version UID for this serializable class. 130 */ 131 private static final long serialVersionUID = 5165171788442351399L; 132 133 134 135 // Indicates whether communication with the upstream server is secure. 136 @Nullable private final Boolean upstreamServerSecure; 137 138 // The upstream response, if available. 139 @Nullable private final IntermediateClientResponseValue upstreamResponse; 140 141 // The server name, which describes the server application, if present. 142 @Nullable private final String serverName; 143 144 // The server response ID, if present. 145 @Nullable private final String serverResponseID; 146 147 // The server session ID, if present. 148 @Nullable private final String serverSessionID; 149 150 // The address of the upstream server, if available. 151 @Nullable private final String upstreamServerAddress; 152 153 154 155 /** 156 * Creates a new intermediate client response value with the provided 157 * information. 158 * 159 * @param upstreamResponse A wrapped intermediate client response from 160 * an upstream server. It may be {@code null} 161 * if there is no wrapped upstream response. 162 * @param upstreamServerAddress The IP address or resolvable name of the 163 * upstream server system. It may be 164 * {@code null} if there is no upstream server 165 * or its address is not available. 166 * @param upstreamServerSecure Indicates whether communication with the 167 * upstream server is secure. It may be 168 * {@code null} if there is no upstream server 169 * or it is not known whether the communication 170 * is secure. 171 * @param serverName An identifier string that summarizes the 172 * server application that created this 173 * intermediate client response. It may be 174 * {@code null} if that information is not 175 * available. 176 * @param serverSessionID A string that may be used to identify the 177 * session in the server application. It may 178 * be {@code null} if there is no available 179 * session identifier. 180 * @param serverResponseID A string that may be used to identify the 181 * response in the server application. It may 182 * be {@code null} if there is no available 183 * response identifier. 184 */ 185 public IntermediateClientResponseValue( 186 @Nullable final IntermediateClientResponseValue upstreamResponse, 187 @Nullable final String upstreamServerAddress, 188 @Nullable final Boolean upstreamServerSecure, 189 @Nullable final String serverName, 190 @Nullable final String serverSessionID, 191 @Nullable final String serverResponseID) 192 { 193 this.upstreamResponse = upstreamResponse; 194 this.upstreamServerAddress = upstreamServerAddress; 195 this.upstreamServerSecure = upstreamServerSecure; 196 this.serverName = serverName; 197 this.serverSessionID = serverSessionID; 198 this.serverResponseID = serverResponseID; 199 } 200 201 202 203 /** 204 * Retrieves the wrapped response from an upstream server, if available. 205 * 206 * @return The wrapped response from an upstream server, or {@code null} if 207 * there is none. 208 */ 209 @Nullable() 210 public IntermediateClientResponseValue getUpstreamResponse() 211 { 212 return upstreamResponse; 213 } 214 215 216 217 /** 218 * Retrieves the IP address or resolvable name of the upstream server system, 219 * if available. 220 * 221 * @return The IP address or resolvable name of the upstream server system, 222 * {@code null} if there is no upstream server or its address is not 223 * available. 224 */ 225 @Nullable() 226 public String getUpstreamServerAddress() 227 { 228 return upstreamServerAddress; 229 } 230 231 232 233 /** 234 * Indicates whether the communication with the communication with the 235 * upstream server is secure (i.e., whether communication between the 236 * server application and the upstream server is safe from interpretation or 237 * undetectable alteration by a third party observer or interceptor). 238 * 239 * 240 * @return {@code Boolean.TRUE} if communication with the upstream server is 241 * secure, {@code Boolean.FALSE} if it is not secure, or 242 * {@code null} if there is no upstream server or it is not known 243 * whether the communication is secure. 244 */ 245 @Nullable() 246 public Boolean upstreamServerSecure() 247 { 248 return upstreamServerSecure; 249 } 250 251 252 253 /** 254 * Retrieves a string that identifies the server application that created this 255 * intermediate client response value. 256 * 257 * @return A string that may be used to identify the server application that 258 * created this intermediate client response value. 259 */ 260 @Nullable() 261 public String getServerName() 262 { 263 return serverName; 264 } 265 266 267 268 /** 269 * Retrieves a string that may be used to identify the session in the server 270 * application. 271 * 272 * @return A string that may be used to identify the session in the server 273 * application, or {@code null} if there is none. 274 */ 275 @Nullable() 276 public String getServerSessionID() 277 { 278 return serverSessionID; 279 } 280 281 282 283 /** 284 * Retrieves a string that may be used to identify the response in the server 285 * application. 286 * 287 * @return A string that may be used to identify the response in the server 288 * application, or {@code null} if there is none. 289 */ 290 @Nullable() 291 public String getServerResponseID() 292 { 293 return serverResponseID; 294 } 295 296 297 298 /** 299 * Encodes this intermediate client response value to a form that may be 300 * included in the response control. 301 * 302 * @return An ASN.1 octet string containing the encoded client response 303 * value. 304 */ 305 @NotNull() 306 public ASN1Sequence encode() 307 { 308 return encode(ASN1Constants.UNIVERSAL_SEQUENCE_TYPE); 309 } 310 311 312 313 /** 314 * Encodes this intermediate client response value to a form that may be 315 * included in the response control. 316 * 317 * @param type The BER type to use for this element. 318 * 319 * @return An ASN.1 octet string containing the encoded client response 320 * value. 321 */ 322 @NotNull() 323 private ASN1Sequence encode(final byte type) 324 { 325 final ArrayList<ASN1Element> elements = new ArrayList<>(6); 326 327 if (upstreamResponse != null) 328 { 329 elements.add(upstreamResponse.encode(TYPE_UPSTREAM_RESPONSE)); 330 } 331 332 if (upstreamServerAddress != null) 333 { 334 elements.add(new ASN1OctetString(TYPE_UPSTREAM_SERVER_ADDRESS, 335 upstreamServerAddress)); 336 } 337 338 if (upstreamServerSecure != null) 339 { 340 elements.add(new ASN1Boolean(TYPE_UPSTREAM_SERVER_SECURE, 341 upstreamServerSecure)); 342 } 343 344 if (serverName != null) 345 { 346 elements.add(new ASN1OctetString(TYPE_SERVER_NAME, serverName)); 347 } 348 349 if (serverSessionID != null) 350 { 351 elements.add(new ASN1OctetString(TYPE_SERVER_SESSION_ID, 352 serverSessionID)); 353 } 354 355 if (serverResponseID != null) 356 { 357 elements.add(new ASN1OctetString(TYPE_SERVER_RESPONSE_ID, 358 serverResponseID)); 359 } 360 361 return new ASN1Sequence(type, elements); 362 } 363 364 365 366 /** 367 * Decodes the provided ASN.1 sequence as an intermediate client response 368 * value. 369 * 370 * @param sequence The sequence to be decoded as an intermediate client 371 * response value. 372 * 373 * @return The decoded intermediate client response value. 374 * 375 * @throws LDAPException If the provided sequence cannot be decoded as an 376 * intermediate client response value. 377 */ 378 @NotNull() 379 public static IntermediateClientResponseValue decode( 380 @NotNull final ASN1Sequence sequence) 381 throws LDAPException 382 { 383 Boolean upstreamServerSecure = null; 384 IntermediateClientResponseValue upstreamResponse = null; 385 String upstreamServerAddress = null; 386 String serverName = null; 387 String serverResponseID = null; 388 String serverSessionID = null; 389 390 for (final ASN1Element element : sequence.elements()) 391 { 392 switch (element.getType()) 393 { 394 case TYPE_UPSTREAM_RESPONSE: 395 try 396 { 397 final ASN1Sequence s = ASN1Sequence.decodeAsSequence(element); 398 upstreamResponse = decode(s); 399 } 400 catch (final LDAPException le) 401 { 402 Debug.debugException(le); 403 throw new LDAPException(ResultCode.DECODING_ERROR, 404 ERR_ICRESP_CANNOT_DECODE_UPSTREAM_RESPONSE.get( 405 le.getMessage()), le); 406 } 407 catch (final Exception e) 408 { 409 Debug.debugException(e); 410 throw new LDAPException(ResultCode.DECODING_ERROR, 411 ERR_ICRESP_CANNOT_DECODE_UPSTREAM_RESPONSE.get( 412 StaticUtils.getExceptionMessage(e)), 413 e); 414 } 415 break; 416 417 case TYPE_UPSTREAM_SERVER_ADDRESS: 418 upstreamServerAddress = 419 ASN1OctetString.decodeAsOctetString(element).stringValue(); 420 break; 421 422 case TYPE_UPSTREAM_SERVER_SECURE: 423 try 424 { 425 upstreamServerSecure = 426 ASN1Boolean.decodeAsBoolean(element).booleanValue(); 427 } 428 catch (final Exception e) 429 { 430 Debug.debugException(e); 431 throw new LDAPException(ResultCode.DECODING_ERROR, 432 ERR_ICRESP_CANNOT_DECODE_UPSTREAM_SECURE.get( 433 StaticUtils.getExceptionMessage(e)), 434 e); 435 } 436 break; 437 438 case TYPE_SERVER_NAME: 439 serverName = 440 ASN1OctetString.decodeAsOctetString(element).stringValue(); 441 break; 442 443 case TYPE_SERVER_SESSION_ID: 444 serverSessionID = 445 ASN1OctetString.decodeAsOctetString(element).stringValue(); 446 break; 447 448 case TYPE_SERVER_RESPONSE_ID: 449 serverResponseID = 450 ASN1OctetString.decodeAsOctetString(element).stringValue(); 451 break; 452 453 default: 454 throw new LDAPException(ResultCode.DECODING_ERROR, 455 ERR_ICRESP_INVALID_ELEMENT_TYPE.get( 456 StaticUtils.toHex(element.getType()))); 457 } 458 } 459 460 return new IntermediateClientResponseValue(upstreamResponse, 461 upstreamServerAddress, 462 upstreamServerSecure, 463 serverName, serverSessionID, 464 serverResponseID); 465 } 466 467 468 469 /** 470 * Generates a hash code for this intermediate client response value. 471 * 472 * @return A hash code for this intermediate client response value. 473 */ 474 @Override() 475 public int hashCode() 476 { 477 int hashCode = 0; 478 479 if (upstreamResponse != null) 480 { 481 hashCode += upstreamResponse.hashCode(); 482 } 483 484 if (upstreamServerAddress != null) 485 { 486 hashCode += upstreamServerAddress.hashCode(); 487 } 488 489 if (upstreamServerSecure != null) 490 { 491 hashCode += upstreamServerSecure.hashCode(); 492 } 493 494 if (serverName != null) 495 { 496 hashCode += serverName.hashCode(); 497 } 498 499 if (serverSessionID != null) 500 { 501 hashCode += serverSessionID.hashCode(); 502 } 503 504 if (serverResponseID != null) 505 { 506 hashCode += serverResponseID.hashCode(); 507 } 508 509 return hashCode; 510 } 511 512 513 514 /** 515 * Indicates whether the provided object is equal to this intermediate client 516 * response value. It will only be considered equal if the provided object is 517 * also an intermediate client response value with all the same fields. 518 * 519 * @param o The object for which to make the determination. 520 * 521 * @return {@code true} if the provided object is considered equal to this 522 * intermediate client response value, or {@code false} if not. 523 */ 524 @Override() 525 public boolean equals(@Nullable final Object o) 526 { 527 if (o == this) 528 { 529 return true; 530 } 531 else if (o == null) 532 { 533 return false; 534 } 535 else if (! (o instanceof IntermediateClientResponseValue)) 536 { 537 return false; 538 } 539 540 final IntermediateClientResponseValue v = 541 (IntermediateClientResponseValue) o; 542 543 if (upstreamResponse == null) 544 { 545 if (v.upstreamResponse != null) 546 { 547 return false; 548 } 549 } 550 else 551 { 552 if (! upstreamResponse.equals(v.upstreamResponse)) 553 { 554 return false; 555 } 556 } 557 558 if (upstreamServerAddress == null) 559 { 560 if (v.upstreamServerAddress != null) 561 { 562 return false; 563 } 564 } 565 else 566 { 567 if (! upstreamServerAddress.equals(v.upstreamServerAddress)) 568 { 569 return false; 570 } 571 } 572 573 if (upstreamServerSecure == null) 574 { 575 if (v.upstreamServerSecure != null) 576 { 577 return false; 578 } 579 } 580 else 581 { 582 if (! upstreamServerSecure.equals(v.upstreamServerSecure)) 583 { 584 return false; 585 } 586 } 587 588 if (serverName == null) 589 { 590 if (v.serverName != null) 591 { 592 return false; 593 } 594 } 595 else 596 { 597 if (! serverName.equals(v.serverName)) 598 { 599 return false; 600 } 601 } 602 603 if (serverSessionID == null) 604 { 605 if (v.serverSessionID != null) 606 { 607 return false; 608 } 609 } 610 else 611 { 612 if (! serverSessionID.equals(v.serverSessionID)) 613 { 614 return false; 615 } 616 } 617 618 if (serverResponseID == null) 619 { 620 if (v.serverResponseID != null) 621 { 622 return false; 623 } 624 } 625 else 626 { 627 if (! serverResponseID.equals(v.serverResponseID)) 628 { 629 return false; 630 } 631 } 632 633 return true; 634 } 635 636 637 638 /** 639 * Retrieves a string representation of this intermediate client response 640 * value. 641 * 642 * @return A string representation of this intermediate client response 643 * value. 644 */ 645 @Override() 646 @NotNull() 647 public String toString() 648 { 649 final StringBuilder buffer = new StringBuilder(); 650 toString(buffer); 651 return buffer.toString(); 652 } 653 654 655 656 /** 657 * Appends a string representation of this intermediate client response value 658 * to the provided buffer. 659 * 660 * @param buffer The buffer to which the information is to be appended. 661 */ 662 public void toString(@NotNull final StringBuilder buffer) 663 { 664 buffer.append("IntermediateClientResponseValue("); 665 666 boolean added = false; 667 if (upstreamResponse != null) 668 { 669 buffer.append("upstreamResponse="); 670 upstreamResponse.toString(buffer); 671 added = true; 672 } 673 674 if (upstreamServerAddress != null) 675 { 676 if (added) 677 { 678 buffer.append(", "); 679 } 680 else 681 { 682 added = true; 683 } 684 685 buffer.append("upstreamServerAddress='"); 686 buffer.append(upstreamServerAddress); 687 buffer.append('\''); 688 } 689 690 if (upstreamServerSecure != null) 691 { 692 if (added) 693 { 694 buffer.append(", "); 695 } 696 else 697 { 698 added = true; 699 } 700 701 buffer.append("upstreamServerSecure='"); 702 buffer.append(upstreamServerSecure); 703 buffer.append('\''); 704 } 705 706 if (serverName != null) 707 { 708 if (added) 709 { 710 buffer.append(", "); 711 } 712 else 713 { 714 added = true; 715 } 716 717 buffer.append("serverName='"); 718 buffer.append(serverName); 719 buffer.append('\''); 720 } 721 722 if (serverSessionID != null) 723 { 724 if (added) 725 { 726 buffer.append(", "); 727 } 728 else 729 { 730 added = true; 731 } 732 733 buffer.append("serverSessionID='"); 734 buffer.append(serverSessionID); 735 buffer.append('\''); 736 } 737 738 if (serverResponseID != null) 739 { 740 if (added) 741 { 742 buffer.append(", "); 743 } 744 else 745 { 746 added = true; 747 } 748 749 buffer.append("serverResponseID='"); 750 buffer.append(serverResponseID); 751 buffer.append('\''); 752 } 753 754 buffer.append(')'); 755 } 756}