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 request value. It may recursively embed intermediate 065 * client request values from downstream clients. 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 IntermediateClientRequestValue 084 implements Serializable 085{ 086 /** 087 * The BER type for the downstreamRequest element. 088 */ 089 private static final byte TYPE_DOWNSTREAM_REQUEST = (byte) 0xA0; 090 091 092 093 /** 094 * The BER type for the downstreamClientAddress element. 095 */ 096 private static final byte TYPE_DOWNSTREAM_CLIENT_ADDRESS = (byte) 0x81; 097 098 099 100 /** 101 * The BER type for the downstreamClientSecure element. 102 */ 103 private static final byte TYPE_DOWNSTREAM_CLIENT_SECURE = (byte) 0x82; 104 105 106 107 /** 108 * The BER type for the clientIdentity element. 109 */ 110 private static final byte TYPE_CLIENT_IDENTITY = (byte) 0x83; 111 112 113 114 /** 115 * The BER type for the clientName element. 116 */ 117 private static final byte TYPE_CLIENT_NAME = (byte) 0x84; 118 119 120 121 /** 122 * The BER type for the clientSessionID element. 123 */ 124 private static final byte TYPE_CLIENT_SESSION_ID = (byte) 0x85; 125 126 127 128 /** 129 * The BER type for the clientRequestID element. 130 */ 131 private static final byte TYPE_CLIENT_REQUEST_ID = (byte) 0x86; 132 133 134 135 /** 136 * The serial version UID for this serializable class. 137 */ 138 private static final long serialVersionUID = -794887520013838259L; 139 140 141 142 // Indicates whether the communication with the downstream client is secure. 143 @Nullable private final Boolean downstreamClientSecure; 144 145 // The downstream request value, if present. 146 @Nullable private final IntermediateClientRequestValue downstreamRequest; 147 148 // The requested client authorization identity, if present. 149 @Nullable private final String clientIdentity; 150 151 // The downstream client address, if present. 152 @Nullable private final String downstreamClientAddress; 153 154 // The client name, which describes the client application, if present. 155 @Nullable private final String clientName; 156 157 // The client request ID, if present. 158 @Nullable private final String clientRequestID; 159 160 // The client session ID, if present. 161 @Nullable private final String clientSessionID; 162 163 164 165 /** 166 * Creates a new intermediate client request value with the provided 167 * information. 168 * 169 * @param downstreamRequest A wrapped intermediate client request from 170 * a downstream client. It may be 171 * {@code null} if there is no downstream 172 * request. 173 * @param downstreamClientAddress The IP address or resolvable name of the 174 * downstream client system. It may be 175 * {@code null} if there is no downstream 176 * client or its address is not available. 177 * @param downstreamClientSecure Indicates whether communication with the 178 * downstream client is secure. It may be 179 * {@code null} if there is no downstream 180 * client or it is not known whether the 181 * communication is secure. 182 * @param clientIdentity The requested client authorization 183 * identity. It may be {@code null} if there 184 * is no requested authorization identity. 185 * @param clientName An identifier string that summarizes the 186 * client application that created this 187 * intermediate client request. It may be 188 * {@code null} if that information is not 189 * available. 190 * @param clientSessionID A string that may be used to identify the 191 * session in the client application. It may 192 * be {@code null} if there is no available 193 * session identifier. 194 * @param clientRequestID A string that may be used to identify the 195 * request in the client application. It may 196 * be {@code null} if there is no available 197 * request identifier. 198 */ 199 public IntermediateClientRequestValue( 200 @Nullable final IntermediateClientRequestValue downstreamRequest, 201 @Nullable final String downstreamClientAddress, 202 @Nullable final Boolean downstreamClientSecure, 203 @Nullable final String clientIdentity, 204 @Nullable final String clientName, 205 @Nullable final String clientSessionID, 206 @Nullable final String clientRequestID) 207 { 208 this.downstreamRequest = downstreamRequest; 209 this.downstreamClientAddress = downstreamClientAddress; 210 this.downstreamClientSecure = downstreamClientSecure; 211 this.clientIdentity = clientIdentity; 212 this.clientName = clientName; 213 this.clientSessionID = clientSessionID; 214 this.clientRequestID = clientRequestID; 215 } 216 217 218 219 /** 220 * Retrieves the wrapped request from a downstream client, if available. 221 * 222 * @return The wrapped request from a downstream client, or {@code null} if 223 * there is none. 224 */ 225 @Nullable() 226 public IntermediateClientRequestValue getDownstreamRequest() 227 { 228 return downstreamRequest; 229 } 230 231 232 233 /** 234 * Retrieves the requested client authorization identity, if available. 235 * 236 * @return The requested client authorization identity, or {@code null} if 237 * there is none. 238 */ 239 @Nullable() 240 public String getClientIdentity() 241 { 242 return clientIdentity; 243 } 244 245 246 247 /** 248 * Retrieves the IP address or resolvable name of the downstream client 249 * system, if available. 250 * 251 * @return The IP address or resolvable name of the downstream client system, 252 * or {@code null} if there is no downstream client or its address is 253 * not available. 254 */ 255 @Nullable() 256 public String getDownstreamClientAddress() 257 { 258 return downstreamClientAddress; 259 } 260 261 262 263 /** 264 * Indicates whether the communication with the communication with the 265 * downstream client is secure (i.e., whether communication between the 266 * client application and the downstream client is safe from interpretation or 267 * undetectable alteration by a third party observer or interceptor). 268 * 269 * 270 * @return {@code Boolean.TRUE} if communication with the downstream client 271 * is secure, {@code Boolean.FALSE} if it is not secure, or 272 * {@code null} if there is no downstream client or it is not known 273 * whether the communication is secure. 274 */ 275 @Nullable() 276 public Boolean downstreamClientSecure() 277 { 278 return downstreamClientSecure; 279 } 280 281 282 283 /** 284 * Retrieves a string that identifies the client application that created this 285 * intermediate client request value. 286 * 287 * @return A string that may be used to identify the client application that 288 * created this intermediate client request value. 289 */ 290 @Nullable() 291 public String getClientName() 292 { 293 return clientName; 294 } 295 296 297 298 /** 299 * Retrieves a string that may be used to identify the session in the client 300 * application. 301 * 302 * @return A string that may be used to identify the session in the client 303 * application, or {@code null} if there is none. 304 */ 305 @Nullable() 306 public String getClientSessionID() 307 { 308 return clientSessionID; 309 } 310 311 312 313 /** 314 * Retrieves a string that may be used to identify the request in the client 315 * application. 316 * 317 * @return A string that may be used to identify the request in the client 318 * application, or {@code null} if there is none. 319 */ 320 @Nullable() 321 public String getClientRequestID() 322 { 323 return clientRequestID; 324 } 325 326 327 328 /** 329 * Encodes this intermediate client request value to a form that may be 330 * included in the request control. 331 * 332 * @return An ASN.1 octet string containing the encoded client request value. 333 */ 334 @NotNull() 335 public ASN1Sequence encode() 336 { 337 return encode(ASN1Constants.UNIVERSAL_SEQUENCE_TYPE); 338 } 339 340 341 342 /** 343 * Encodes this intermediate client request value to a form that may be 344 * included in the request control. 345 * 346 * @param type The BER type to use for this element. 347 * 348 * @return An ASN.1 octet string containing the encoded client request value. 349 */ 350 @NotNull() 351 private ASN1Sequence encode(final byte type) 352 { 353 final ArrayList<ASN1Element> elements = new ArrayList<>(7); 354 355 if (downstreamRequest != null) 356 { 357 elements.add(downstreamRequest.encode(TYPE_DOWNSTREAM_REQUEST)); 358 } 359 360 if (downstreamClientAddress != null) 361 { 362 elements.add(new ASN1OctetString(TYPE_DOWNSTREAM_CLIENT_ADDRESS, 363 downstreamClientAddress)); 364 } 365 366 if (downstreamClientSecure != null) 367 { 368 elements.add(new ASN1Boolean(TYPE_DOWNSTREAM_CLIENT_SECURE, 369 downstreamClientSecure)); 370 } 371 372 if (clientIdentity != null) 373 { 374 elements.add(new ASN1OctetString(TYPE_CLIENT_IDENTITY, clientIdentity)); 375 } 376 377 if (clientName != null) 378 { 379 elements.add(new ASN1OctetString(TYPE_CLIENT_NAME, clientName)); 380 } 381 382 if (clientSessionID != null) 383 { 384 elements.add(new ASN1OctetString(TYPE_CLIENT_SESSION_ID, 385 clientSessionID)); 386 } 387 388 if (clientRequestID != null) 389 { 390 elements.add(new ASN1OctetString(TYPE_CLIENT_REQUEST_ID, 391 clientRequestID)); 392 } 393 394 return new ASN1Sequence(type, elements); 395 } 396 397 398 399 /** 400 * Decodes the provided ASN.1 sequence as an intermediate client request 401 * value. 402 * 403 * @param sequence The sequence to be decoded as an intermediate client 404 * request value. 405 * 406 * @return The decoded intermediate client request value. 407 * 408 * @throws LDAPException If the provided sequence cannot be decoded as an 409 * intermediate client request value. 410 */ 411 @NotNull() 412 public static IntermediateClientRequestValue decode( 413 @NotNull final ASN1Sequence sequence) 414 throws LDAPException 415 { 416 Boolean downstreamClientSecure = null; 417 IntermediateClientRequestValue downstreamRequest = null; 418 String clientIdentity = null; 419 String downstreamClientAddress = null; 420 String clientName = null; 421 String clientRequestID = null; 422 String clientSessionID = null; 423 424 for (final ASN1Element element : sequence.elements()) 425 { 426 switch (element.getType()) 427 { 428 case TYPE_DOWNSTREAM_REQUEST: 429 try 430 { 431 final ASN1Sequence s = ASN1Sequence.decodeAsSequence(element); 432 downstreamRequest = decode(s); 433 } 434 catch (final LDAPException le) 435 { 436 Debug.debugException(le); 437 throw new LDAPException(ResultCode.DECODING_ERROR, 438 ERR_ICREQ_CANNOT_DECODE_DOWNSTREAM_REQUEST.get( 439 le.getMessage()), le); 440 } 441 catch (final Exception e) 442 { 443 Debug.debugException(e); 444 throw new LDAPException(ResultCode.DECODING_ERROR, 445 ERR_ICREQ_CANNOT_DECODE_DOWNSTREAM_REQUEST.get( 446 StaticUtils.getExceptionMessage(e)), 447 e); 448 } 449 break; 450 451 case TYPE_DOWNSTREAM_CLIENT_ADDRESS: 452 downstreamClientAddress = 453 ASN1OctetString.decodeAsOctetString(element).stringValue(); 454 break; 455 456 case TYPE_DOWNSTREAM_CLIENT_SECURE: 457 try 458 { 459 downstreamClientSecure = 460 ASN1Boolean.decodeAsBoolean(element).booleanValue(); 461 } 462 catch (final Exception e) 463 { 464 Debug.debugException(e); 465 throw new LDAPException(ResultCode.DECODING_ERROR, 466 ERR_ICREQ_CANNOT_DECODE_DOWNSTREAM_SECURE.get( 467 StaticUtils.getExceptionMessage(e)), 468 e); 469 } 470 break; 471 472 case TYPE_CLIENT_IDENTITY: 473 clientIdentity = 474 ASN1OctetString.decodeAsOctetString(element).stringValue(); 475 break; 476 477 case TYPE_CLIENT_NAME: 478 clientName = 479 ASN1OctetString.decodeAsOctetString(element).stringValue(); 480 break; 481 482 case TYPE_CLIENT_SESSION_ID: 483 clientSessionID = 484 ASN1OctetString.decodeAsOctetString(element).stringValue(); 485 break; 486 487 case TYPE_CLIENT_REQUEST_ID: 488 clientRequestID = 489 ASN1OctetString.decodeAsOctetString(element).stringValue(); 490 break; 491 492 default: 493 throw new LDAPException(ResultCode.DECODING_ERROR, 494 ERR_ICREQ_INVALID_ELEMENT_TYPE.get( 495 StaticUtils.toHex(element.getType()))); 496 } 497 } 498 499 return new IntermediateClientRequestValue(downstreamRequest, 500 downstreamClientAddress, 501 downstreamClientSecure, 502 clientIdentity, clientName, 503 clientSessionID, clientRequestID); 504 } 505 506 507 508 /** 509 * Generates a hash code for this intermediate client request value. 510 * 511 * @return A hash code for this intermediate client request value. 512 */ 513 @Override() 514 public int hashCode() 515 { 516 int hashCode = 0; 517 518 if (downstreamRequest != null) 519 { 520 hashCode += downstreamRequest.hashCode(); 521 } 522 523 if (downstreamClientAddress != null) 524 { 525 hashCode += downstreamClientAddress.hashCode(); 526 } 527 528 if (downstreamClientSecure != null) 529 { 530 hashCode += downstreamClientSecure.hashCode(); 531 } 532 533 if (clientIdentity != null) 534 { 535 hashCode += clientIdentity.hashCode(); 536 } 537 538 if (clientName != null) 539 { 540 hashCode += clientName.hashCode(); 541 } 542 543 if (clientSessionID != null) 544 { 545 hashCode += clientSessionID.hashCode(); 546 } 547 548 if (clientRequestID != null) 549 { 550 hashCode += clientRequestID.hashCode(); 551 } 552 553 return hashCode; 554 } 555 556 557 558 /** 559 * Indicates whether the provided object is equal to this intermediate client 560 * request value. It will only be considered equal if the provided object is 561 * also an intermediate client request value with all the same fields. 562 * 563 * @param o The object for which to make the determination. 564 * 565 * @return {@code true} if the provided object is considered equal to this 566 * intermediate client request value, or {@code false} if not. 567 */ 568 @Override() 569 public boolean equals(@Nullable final Object o) 570 { 571 if (o == this) 572 { 573 return true; 574 } 575 else if (o == null) 576 { 577 return false; 578 } 579 else if (! (o instanceof IntermediateClientRequestValue)) 580 { 581 return false; 582 } 583 584 final IntermediateClientRequestValue v = (IntermediateClientRequestValue) o; 585 586 if (downstreamRequest == null) 587 { 588 if (v.downstreamRequest != null) 589 { 590 return false; 591 } 592 } 593 else 594 { 595 if (! downstreamRequest.equals(v.downstreamRequest)) 596 { 597 return false; 598 } 599 } 600 601 if (downstreamClientAddress == null) 602 { 603 if (v.downstreamClientAddress != null) 604 { 605 return false; 606 } 607 } 608 else 609 { 610 if (! downstreamClientAddress.equals(v.downstreamClientAddress)) 611 { 612 return false; 613 } 614 } 615 616 if (downstreamClientSecure == null) 617 { 618 if (v.downstreamClientSecure != null) 619 { 620 return false; 621 } 622 } 623 else 624 { 625 if (! downstreamClientSecure.equals(v.downstreamClientSecure)) 626 { 627 return false; 628 } 629 } 630 631 if (clientIdentity == null) 632 { 633 if (v.clientIdentity != null) 634 { 635 return false; 636 } 637 } 638 else 639 { 640 if (! clientIdentity.equals(v.clientIdentity)) 641 { 642 return false; 643 } 644 } 645 646 if (clientName == null) 647 { 648 if (v.clientName != null) 649 { 650 return false; 651 } 652 } 653 else 654 { 655 if (! clientName.equals(v.clientName)) 656 { 657 return false; 658 } 659 } 660 661 if (clientSessionID == null) 662 { 663 if (v.clientSessionID != null) 664 { 665 return false; 666 } 667 } 668 else 669 { 670 if (! clientSessionID.equals(v.clientSessionID)) 671 { 672 return false; 673 } 674 } 675 676 if (clientRequestID == null) 677 { 678 if (v.clientRequestID != null) 679 { 680 return false; 681 } 682 } 683 else 684 { 685 if (! clientRequestID.equals(v.clientRequestID)) 686 { 687 return false; 688 } 689 } 690 691 return true; 692 } 693 694 695 696 /** 697 * Retrieves a string representation of this intermediate client request 698 * value. 699 * 700 * @return A string representation of this intermediate client request value. 701 */ 702 @Override() 703 @NotNull() 704 public String toString() 705 { 706 final StringBuilder buffer = new StringBuilder(); 707 toString(buffer); 708 return buffer.toString(); 709 } 710 711 712 713 /** 714 * Appends a string representation of this intermediate client request value 715 * to the provided buffer. 716 * 717 * @param buffer The buffer to which the information is to be appended. 718 */ 719 public void toString(@NotNull final StringBuilder buffer) 720 { 721 buffer.append("IntermediateClientRequestValue("); 722 723 boolean added = false; 724 if (downstreamRequest != null) 725 { 726 buffer.append("downstreamRequest="); 727 downstreamRequest.toString(buffer); 728 added = true; 729 } 730 731 if (clientIdentity != null) 732 { 733 if (added) 734 { 735 buffer.append(", "); 736 } 737 else 738 { 739 added = true; 740 } 741 742 buffer.append("clientIdentity='"); 743 buffer.append(clientIdentity); 744 buffer.append('\''); 745 } 746 747 if (downstreamClientAddress != null) 748 { 749 if (added) 750 { 751 buffer.append(", "); 752 } 753 else 754 { 755 added = true; 756 } 757 758 buffer.append("downstreamClientAddress='"); 759 buffer.append(downstreamClientAddress); 760 buffer.append('\''); 761 } 762 763 if (downstreamClientSecure != null) 764 { 765 if (added) 766 { 767 buffer.append(", "); 768 } 769 else 770 { 771 added = true; 772 } 773 774 buffer.append("downstreamClientSecure='"); 775 buffer.append(downstreamClientSecure); 776 buffer.append('\''); 777 } 778 779 if (clientName != null) 780 { 781 if (added) 782 { 783 buffer.append(", "); 784 } 785 else 786 { 787 added = true; 788 } 789 790 buffer.append("clientName='"); 791 buffer.append(clientName); 792 buffer.append('\''); 793 } 794 795 if (clientSessionID != null) 796 { 797 if (added) 798 { 799 buffer.append(", "); 800 } 801 else 802 { 803 added = true; 804 } 805 806 buffer.append("clientSessionID='"); 807 buffer.append(clientSessionID); 808 buffer.append('\''); 809 } 810 811 if (clientRequestID != null) 812 { 813 if (added) 814 { 815 buffer.append(", "); 816 } 817 else 818 { 819 added = true; 820 } 821 822 buffer.append("clientRequestID='"); 823 buffer.append(clientRequestID); 824 buffer.append('\''); 825 } 826 827 buffer.append(')'); 828 } 829}