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}